diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index ecbf345d4..5e5dc5d04 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -6,6 +6,7 @@ import ( "fmt" "strconv" "testing" + "time" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -21,6 +22,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // DefaultConsensusParams defines the default Tendermint consensus params used in @@ -67,6 +69,77 @@ func Setup(isCheckTx bool) *SimApp { return app } +// SetupWithGenesisValSet initializes a new SimApp with a validator set and genesis accounts +// that also act as delegators. For simplicity, each validator is bonded with a delegation +// of one consensus engine unit (10^6) in the default token of the simapp from first genesis +// account. A Nop logger is set in SimApp. +func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { + db := dbm.NewMemDB() + app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5) + + genesisState := NewDefaultGenesisState() + + // set genesis accounts + authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = app.Codec().MustMarshalJSON(authGenesis) + + validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) + delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) + + bondAmt := sdk.NewInt(1000000) + + for _, val := range valSet.Validators { + validator := stakingtypes.Validator{ + OperatorAddress: val.Address.Bytes(), + ConsensusPubkey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, val.PubKey), + Jailed: false, + Status: sdk.Bonded, + Tokens: bondAmt, + DelegatorShares: sdk.OneDec(), + Description: stakingtypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + MinSelfDelegation: sdk.ZeroInt(), + } + validators = append(validators, validator) + delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) + + } + + // set validators and delegations + stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) + genesisState[stakingtypes.ModuleName] = app.Codec().MustMarshalJSON(stakingGenesis) + + totalSupply := sdk.NewCoins() + for _, b := range balances { + // add genesis acc tokens and delegated tokens to total supply + totalSupply = totalSupply.Add(b.Coins.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt))...) + } + + // update total supply + bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().SendEnabled, balances, totalSupply) + genesisState[banktypes.ModuleName] = app.Codec().MustMarshalJSON(bankGenesis) + + stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState) + require.NoError(t, err) + + // init chain will set the validator set and initialize the genesis accounts + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) + + // commit genesis changes + app.Commit() + app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}}) + + return app +} + // SetupWithGenesisAccounts initializes a new SimApp with the provided genesis // accounts and possible balances. func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { diff --git a/x/ibc/04-channel/keeper/handshake_test.go b/x/ibc/04-channel/keeper/handshake_test.go index 508353acf..cc48cf4ab 100644 --- a/x/ibc/04-channel/keeper/handshake_test.go +++ b/x/ibc/04-channel/keeper/handshake_test.go @@ -4,624 +4,643 @@ import ( "fmt" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) -func (suite *KeeperTestSuite) TestChanOpenInit() { - counterparty := types.NewCounterparty(testPort2, testChannel2) - - var portCap *capabilitytypes.Capability - testCases := []testCase{ - {"success", func() { - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, - connection.INIT, - ) - }, true}, - {"channel already exists", func() { - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.INIT, - types.ORDERED, testConnectionIDA, - ) - }, false}, - {"connection doesn't exist", func() {}, false}, - {"connection is UNINITIALIZED", func() { - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, - connection.UNINITIALIZED, - ) - }, false}, - {"capability is incorrect", func() { - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, - connection.INIT, - ) - portCap = capabilitytypes.NewCapability(3) - }, false}, - } - - for i, tc := range testCases { - tc := tc - i := i - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.SetupTest() // reset - - var err error - portCap, err = suite.chainA.App.ScopedIBCKeeper.NewCapability( - suite.chainA.GetContext(), host.PortPath(testPort1), - ) - suite.Require().NoError(err, "could not create capability") - - tc.malleate() - cap, err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanOpenInit( - suite.chainA.GetContext(), types.ORDERED, []string{testConnectionIDA}, - testPort1, testChannel1, portCap, counterparty, testChannelVersion, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().NotNil(cap) - chanCap, ok := suite.chainA.App.ScopedIBCKeeper.GetCapability( - suite.chainA.GetContext(), - host.ChannelCapabilityPath(testPort1, testChannel1), - ) - suite.Require().True(ok, "could not retrieve channel capapbility after successful ChanOpenInit") - suite.Require().Equal(chanCap.String(), cap.String(), "channel capability is not correct") - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - } - }) - } -} - -func (suite *KeeperTestSuite) TestChanOpenTry() { - counterparty := types.NewCounterparty(testPort1, testChannel1) - channelKey := host.KeyChannel(testPort1, testChannel1) - - var portCap *capabilitytypes.Capability - testCases := []testCase{ - {"success", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.INIT, types.ORDERED, testConnectionIDA) - }, true}, - {"previous channel with invalid state", func() { - suite.chainA.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.UNINITIALIZED, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"connection doesn't exist", func() {}, false}, - {"connection is not OPEN", func() { - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.INIT, - ) - }, false}, - {"consensus state not found", func() { - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.OPEN, - ) - }, false}, - {"channel verification failed", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.OPEN, - ) - }, false}, - {"port capability not found", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.INIT, types.ORDERED, testConnectionIDA) - portCap = capabilitytypes.NewCapability(3) - }, false}, - } - - for i, tc := range testCases { - tc := tc - i := i - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.SetupTest() // reset - - var err error - portCap, err = suite.chainA.App.ScopedIBCKeeper.NewCapability(suite.chainA.GetContext(), host.PortPath(testPort2)) - suite.Require().NoError(err, "could not create capability") - - tc.malleate() - - suite.chainA.updateClient(suite.chainB) - suite.chainB.updateClient(suite.chainA) - proof, proofHeight := queryProof(suite.chainB, channelKey) - - if tc.expPass { - cap, err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanOpenTry( - suite.chainA.GetContext(), types.ORDERED, []string{testConnectionIDB}, - testPort2, testChannel2, portCap, counterparty, testChannelVersion, testChannelVersion, - proof, proofHeight+1, - ) - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - suite.Require().NotNil(cap) - chanCap, ok := suite.chainA.App.ScopedIBCKeeper.GetCapability( - suite.chainA.GetContext(), - host.ChannelCapabilityPath(testPort2, testChannel2), - ) - suite.Require().True(ok, "could not retrieve channel capapbility after successful ChanOpenInit") - suite.Require().Equal(chanCap.String(), cap.String(), "channel capability is not correct") - } else { - _, err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanOpenTry( - suite.chainA.GetContext(), types.ORDERED, []string{testConnectionIDB}, - testPort2, testChannel2, portCap, counterparty, testChannelVersion, testChannelVersion, - proof, proofHeight, - ) - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - } - }) - } -} - -func (suite *KeeperTestSuite) TestChanOpenAck() { - channelKey := host.KeyChannel(testPort2, testChannel2) - - var channelCap *capabilitytypes.Capability - testCases := []testCase{ - {"success", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.INIT, - types.ORDERED, testConnectionIDB, - ) - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.TRYOPEN, - types.ORDERED, testConnectionIDA, - ) - }, true}, - {"channel doesn't exist", func() {}, false}, - {"channel state is not INIT or TRYOPEN", func() { - suite.chainB.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.UNINITIALIZED, - types.ORDERED, testConnectionIDA, - ) - }, false}, - {"connection not found", func() { - suite.chainB.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.TRYOPEN, - types.ORDERED, testConnectionIDA, - ) - }, false}, - {"connection is not OPEN", func() { - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.TRYOPEN, - ) - suite.chainB.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.TRYOPEN, - types.ORDERED, testConnectionIDA, - ) - }, false}, - {"consensus state not found", func() { - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainB.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.TRYOPEN, - types.ORDERED, testConnectionIDA, - ) - }, false}, - {"channel verification failed", func() { - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainB.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.TRYOPEN, - types.ORDERED, testConnectionIDA, - ) - }, false}, - {"channel capability not found", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.INIT, - types.ORDERED, testConnectionIDB, - ) - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.TRYOPEN, - types.ORDERED, testConnectionIDA, - ) - channelCap = capabilitytypes.NewCapability(3) - }, false}, - } - - for i, tc := range testCases { - tc := tc - i := i - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.SetupTest() // reset - - var err error - channelCap, err = suite.chainA.App.ScopedIBCKeeper.NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(testPort1, testChannel1)) - suite.Require().NoError(err, "could not create capability") - - tc.malleate() - - suite.chainA.updateClient(suite.chainB) - suite.chainB.updateClient(suite.chainA) - proof, proofHeight := queryProof(suite.chainB, channelKey) - - if tc.expPass { - err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanOpenAck( - suite.chainA.GetContext(), testPort1, testChannel1, channelCap, testChannelVersion, - proof, proofHeight+1, - ) - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - } else { - err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanOpenAck( - suite.chainA.GetContext(), testPort1, testChannel1, channelCap, testChannelVersion, - proof, proofHeight+1, - ) - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - } - }) - } -} - -func (suite *KeeperTestSuite) TestChanOpenConfirm() { - channelKey := host.KeyChannel(testPort2, testChannel2) - - var channelCap *capabilitytypes.Capability - testCases := []testCase{ - {"success", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.TRYOPEN, - ) - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.OPEN, - types.ORDERED, testConnectionIDB, - ) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, - types.TRYOPEN, types.ORDERED, testConnectionIDA) - }, true}, - {"channel doesn't exist", func() {}, false}, - {"channel state is not TRYOPEN", func() { - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.UNINITIALIZED, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"connection not found", func() { - suite.chainA.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.TRYOPEN, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"connection is not OPEN", func() { - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.TRYOPEN, - ) - suite.chainA.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.TRYOPEN, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"consensus state not found", func() { - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.TRYOPEN, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"channel verification failed", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.TRYOPEN, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"channel capability not found", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.TRYOPEN, - ) - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.OPEN, - types.ORDERED, testConnectionIDB, - ) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, - types.TRYOPEN, types.ORDERED, testConnectionIDA) - channelCap = capabilitytypes.NewCapability(3) - }, false}, - } - - for i, tc := range testCases { - tc := tc - i := i - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.SetupTest() // reset - - var err error - channelCap, err = suite.chainB.App.ScopedIBCKeeper.NewCapability(suite.chainB.GetContext(), host.ChannelCapabilityPath(testPort1, testChannel1)) - suite.Require().NoError(err, "could not create capability") - - tc.malleate() - - suite.chainA.updateClient(suite.chainB) - suite.chainB.updateClient(suite.chainA) - proof, proofHeight := queryProof(suite.chainA, channelKey) - - if tc.expPass { - err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanOpenConfirm( - suite.chainB.GetContext(), testPort1, testChannel1, - channelCap, proof, proofHeight+1, - ) - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - } else { - err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanOpenConfirm( - suite.chainB.GetContext(), testPort1, testChannel1, channelCap, - proof, proofHeight+1, - ) - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - } - }) - } -} - -func (suite *KeeperTestSuite) TestChanCloseInit() { - var channelCap *capabilitytypes.Capability - testCases := []testCase{ - {"success", func() { - suite.chainB.CreateClient(suite.chainA) - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.OPEN, - types.ORDERED, testConnectionIDA, - ) - }, true}, - {"channel doesn't exist", func() {}, false}, - {"channel state is CLOSED", func() { - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.CLOSED, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"connection not found", func() { - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.OPEN, - types.ORDERED, testConnectionIDA, - ) - }, false}, - {"connection is not OPEN", func() { - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.TRYOPEN, - ) - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.UNINITIALIZED, - types.ORDERED, testConnectionIDA, - ) - }, false}, - {"channel capability not found", func() { - suite.chainB.CreateClient(suite.chainA) - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.OPEN, - types.ORDERED, testConnectionIDA, - ) - channelCap = capabilitytypes.NewCapability(3) - }, false}, - } - - for i, tc := range testCases { - tc := tc - i := i - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.SetupTest() // reset - - var err error - channelCap, err = suite.chainA.App.ScopedIBCKeeper.NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(testPort1, testChannel1)) - suite.Require().NoError(err, "could not create capability") - - tc.malleate() - err = suite.chainA.App.IBCKeeper.ChannelKeeper.ChanCloseInit( - suite.chainA.GetContext(), testPort1, testChannel1, channelCap, - ) - - 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) - } - }) - } -} - -func (suite *KeeperTestSuite) TestChanCloseConfirm() { - channelKey := host.KeyChannel(testPort1, testChannel1) - - var channelCap *capabilitytypes.Capability - testCases := []testCase{ - {"success", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.OPEN, - types.ORDERED, testConnectionIDB, - ) - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.CLOSED, - types.ORDERED, testConnectionIDA, - ) - }, true}, - {"channel doesn't exist", func() {}, false}, - {"channel state is CLOSED", func() { - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.CLOSED, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"connection not found", func() { - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.OPEN, - types.ORDERED, testConnectionIDA, - ) - }, false}, - {"connection is not OPEN", func() { - suite.chainB.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, - connection.TRYOPEN, - ) - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.OPEN, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"consensus state not found", func() { - suite.chainB.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.OPEN, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"channel verification failed", func() { - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.OPEN, - types.ORDERED, testConnectionIDB, - ) - }, false}, - {"channel capability not found", func() { - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.OPEN, - types.ORDERED, testConnectionIDB, - ) - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.CLOSED, - types.ORDERED, testConnectionIDA, - ) - }, false}, - } - - for i, tc := range testCases { - tc := tc - i := i - suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.SetupTest() // reset - - var err error - channelCap, err = suite.chainB.App.ScopedIBCKeeper.NewCapability(suite.chainB.GetContext(), host.ChannelCapabilityPath(testPort2, testChannel2)) - suite.Require().NoError(err, "could not create capability") - - tc.malleate() - - suite.chainA.updateClient(suite.chainB) - suite.chainB.updateClient(suite.chainA) - proof, proofHeight := queryProof(suite.chainA, channelKey) - - if tc.expPass { - err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanCloseConfirm( - suite.chainB.GetContext(), testPort2, testChannel2, channelCap, - proof, proofHeight+1, - ) - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) - } else { - err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanCloseConfirm( - suite.chainB.GetContext(), testPort2, testChannel2, channelCap, - proof, proofHeight, - ) - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) - } - }) - } -} - type testCase = struct { msg string malleate func() expPass bool } + +// TestChanOpenInit tests the OpenInit handshake call for channels. It uses message passing +// to enter into the appropriate state and then calls ChanOpenInit directly. The channel is +// being created on chainA. The port capability must be created on chainA before ChanOpenInit +// can succeed. +func (suite *KeeperTestSuite) TestChanOpenInit() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + portCap *capabilitytypes.Capability + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + suite.chainA.CreatePortCapability(connA.NextTestChannel().PortID) + portCap = suite.chainA.GetPortCapability(connA.NextTestChannel().PortID) + }, true}, + {"channel already exists", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB) + }, false}, + {"connection doesn't exist", func() { + // any non-nil values of connA and connB are acceptable + suite.Require().NotNil(connA) + suite.Require().NotNil(connB) + }, false}, + {"connection is UNINITIALIZED", func() { + // any non-nil values of connA and connB are acceptable + suite.Require().NotNil(connA) + suite.Require().NotNil(connB) + + // set connection as UNINITIALIZED + counterparty := connectiontypes.NewCounterparty(clientIDB, connIDA, suite.chainB.GetPrefix()) + connection := connectiontypes.NewConnectionEnd(connectiontypes.UNINITIALIZED, clientIDA, connIDA, counterparty, []string{ibctesting.ConnectionVersion}) + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, connection) + + portCap = nil + }, false}, + {"capability is incorrect", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + portCap = capabilitytypes.NewCapability(3) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + // run test for all types of ordering + for _, order := range []types.Order{types.UNORDERED, types.ORDERED} { + suite.SetupTest() // reset + tc.malleate() + + counterparty := types.NewCounterparty(connB.FirstOrNextTestChannel().PortID, connB.FirstOrNextTestChannel().ID) + channelA := connA.FirstOrNextTestChannel() + + cap, err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanOpenInit( + suite.chainA.GetContext(), order, []string{connA.ID}, + channelA.PortID, channelA.ID, portCap, counterparty, ibctesting.ChannelVersion, + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + chanCap, ok := suite.chainA.App.ScopedIBCKeeper.GetCapability( + suite.chainA.GetContext(), + host.ChannelCapabilityPath(channelA.PortID, channelA.ID), + ) + suite.Require().True(ok, "could not retrieve channel capability after successful ChanOpenInit") + suite.Require().Equal(chanCap.String(), cap.String(), "channel capability is not correct") + } else { + suite.Require().Error(err) + } + } + }) + } +} + +// TestChanOpenTry tests the OpenTry handshake call for channels. It uses message passing +// to enter into the appropriate state and then calls ChanOpenTry directly. The channel +// is being created on chainB. The port capability must be created on chainB before +// ChanOpenTry can succeed. +func (suite *KeeperTestSuite) TestChanOpenTry() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + portCap *capabilitytypes.Capability + heightDiff uint64 + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + + suite.chainB.CreatePortCapability(connB.NextTestChannel().PortID) + portCap = suite.chainB.GetPortCapability(connB.NextTestChannel().PortID) + }, true}, + {"previous channel with invalid state", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + + // make previous channel have wrong ordering + suite.coordinator.ChanOpenInit(suite.chainB, suite.chainA, connB, connA, types.UNORDERED) + }, false}, + {"connection doesn't exist", func() { + // any non-nil values of connA and connB are acceptable + suite.Require().NotNil(connA) + suite.Require().NotNil(connB) + + // pass capability check + suite.chainB.CreatePortCapability(connB.FirstOrNextTestChannel().PortID) + portCap = suite.chainB.GetPortCapability(connB.FirstOrNextTestChannel().PortID) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + // pass capability check + suite.chainB.CreatePortCapability(connB.FirstOrNextTestChannel().PortID) + portCap = suite.chainB.GetPortCapability(connB.FirstOrNextTestChannel().PortID) + + var err error + connB, connA, err = suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + }, false}, + {"consensus state not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + + suite.chainB.CreatePortCapability(connB.NextTestChannel().PortID) + portCap = suite.chainB.GetPortCapability(connB.NextTestChannel().PortID) + + heightDiff = 3 // consensus state doesn't exist at this height + }, false}, + {"channel verification failed", func() { + // not creating a channel on chainA will result in an invalid proof of existence + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + portCap = suite.chainB.GetPortCapability(connB.NextTestChannel().PortID) + }, false}, + {"port capability not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + + portCap = capabilitytypes.NewCapability(3) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + heightDiff = 0 // must be explicitly changed in malleate + + tc.malleate() + counterparty := types.NewCounterparty(connA.FirstOrNextTestChannel().PortID, connA.FirstOrNextTestChannel().ID) + channelB := connB.FirstOrNextTestChannel() + + channelKey := host.KeyChannel(counterparty.PortID, counterparty.ChannelID) + proof, proofHeight := suite.chainA.QueryProof(channelKey) + + cap, err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanOpenTry( + suite.chainB.GetContext(), types.ORDERED, []string{connB.ID}, + channelB.PortID, channelB.ID, portCap, counterparty, ibctesting.ChannelVersion, ibctesting.ChannelVersion, + proof, proofHeight+heightDiff, + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + chanCap, ok := suite.chainB.App.ScopedIBCKeeper.GetCapability( + suite.chainB.GetContext(), + host.ChannelCapabilityPath(channelB.PortID, channelB.ID), + ) + suite.Require().True(ok, "could not retrieve channel capapbility after successful ChanOpenTry") + suite.Require().Equal(chanCap.String(), cap.String(), "channel capability is not correct") + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestChanOpenAck tests the OpenAck handshake call for channels. It uses message passing +// to enter into the appropriate state and then calls ChanOpenAck directly. The handshake +// call is occurring on chainA. +func (suite *KeeperTestSuite) TestChanOpenAck() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + channelCap *capabilitytypes.Capability + heightDiff uint64 + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"channel doesn't exist", func() {}, false}, + {"channel state is not INIT or TRYOPEN", func() { + // create fully open channels on both chains + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelA := connA.Channels[0] + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"connection not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + + // set the channel's connection hops to wrong connection ID + channel := suite.chainA.GetChannel(channelA) + channel.ConnectionHops[0] = "doesnotexist" + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + + var err error + connA, connB, err = suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // create channel in init + channelA, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"consensus state not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + + heightDiff = 3 // consensus state doesn't exist at this height + }, false}, + {"channel verification failed", func() { + // chainB is INIT, chainA in TRYOPEN + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelB, channelA, err := suite.coordinator.ChanOpenInit(suite.chainB, suite.chainA, connB, connA, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainA, suite.chainB, channelA, channelB, connA, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel capability not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + + channelCap = capabilitytypes.NewCapability(6) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + heightDiff = 0 // must be explicitly changed + tc.malleate() + + channelA := connA.FirstOrNextTestChannel() + channelB := connB.FirstOrNextTestChannel() + + channelKey := host.KeyChannel(channelB.PortID, channelB.ID) + proof, proofHeight := suite.chainB.QueryProof(channelKey) + + err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanOpenAck( + suite.chainA.GetContext(), channelA.PortID, channelA.ID, channelCap, ibctesting.ChannelVersion, + proof, proofHeight+heightDiff, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestChanOpenConfirm tests the OpenAck handshake call for channels. It uses message passing +// to enter into the appropriate state and then calls ChanOpenConfirm directly. The handshake +// call is occurring on chainB. +func (suite *KeeperTestSuite) TestChanOpenConfirm() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + channelCap *capabilitytypes.Capability + heightDiff uint64 + ) + testCases := []testCase{ + {"success", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenAck(suite.chainA, suite.chainB, channelA, channelB) + suite.Require().NoError(err) + + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, true}, + {"channel doesn't exist", func() {}, false}, + {"channel state is not TRYOPEN", func() { + // create fully open channels on both cahins + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelB := connB.Channels[0] + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"connection not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenAck(suite.chainA, suite.chainB, channelA, channelB) + suite.Require().NoError(err) + + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + // set the channel's connection hops to wrong connection ID + channel := suite.chainB.GetChannel(channelB) + channel.ConnectionHops[0] = "doesnotexist" + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainB.GetContext(), channelB.PortID, channelB.ID, channel) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + + var err error + connA, connB, err = suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + channelB := connB.FirstOrNextTestChannel() + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"consensus state not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenAck(suite.chainA, suite.chainB, channelA, channelB) + suite.Require().NoError(err) + + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + heightDiff = 3 + }, false}, + {"channel verification failed", func() { + // chainA is INIT, chainB in TRYOPEN + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"channel capability not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenAck(suite.chainA, suite.chainB, channelA, channelB) + suite.Require().NoError(err) + + channelCap = capabilitytypes.NewCapability(6) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + heightDiff = 0 // must be explicitly changed + + tc.malleate() + + channelA := connA.FirstOrNextTestChannel() + channelB := connB.FirstOrNextTestChannel() + + channelKey := host.KeyChannel(channelA.PortID, channelA.ID) + proof, proofHeight := suite.chainA.QueryProof(channelKey) + + err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanOpenConfirm( + suite.chainB.GetContext(), channelB.PortID, channelB.ID, + channelCap, proof, proofHeight+heightDiff, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestChanCloseInit tests the initial closing of a handshake on chainA by calling +// ChanCloseInit. Both chains will use message passing to setup OPEN channels. +func (suite *KeeperTestSuite) TestChanCloseInit() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + channelCap *capabilitytypes.Capability + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelA := connA.Channels[0] + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"channel doesn't exist", func() { + // any non-nil values work for connections + suite.Require().NotNil(connA) + suite.Require().NotNil(connB) + channelA := connA.FirstOrNextTestChannel() + + // ensure channel capability check passes + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel state is CLOSED", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelA := connA.Channels[0] + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + + // close channel + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + }, false}, + {"connection not found", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelA := connA.Channels[0] + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + + // set the channel's connection hops to wrong connection ID + channel := suite.chainA.GetChannel(channelA) + channel.ConnectionHops[0] = "doesnotexist" + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + + var err error + connA, connB, err = suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // create channel in init + channelA, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + + // ensure channel capability check passes + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel capability not found", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelCap = capabilitytypes.NewCapability(3) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + channelA := connA.FirstOrNextTestChannel() + + err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanCloseInit( + suite.chainA.GetContext(), channelA.PortID, channelA.ID, channelCap, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestChanCloseConfirm tests the confirming closing channel ends by calling ChanCloseConfirm +// on chainB. Both chains will use message passing to setup OPEN channels. ChanCloseInit is +// bypassed on chainA by setting the channel state in the ChannelKeeper. +func (suite *KeeperTestSuite) TestChanCloseConfirm() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + channelA ibctesting.TestChannel + channelB ibctesting.TestChannel + channelCap *capabilitytypes.Capability + heightDiff uint64 + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + }, true}, + {"channel doesn't exist", func() { + // any non-nil values work for connections + suite.Require().NotNil(connA) + suite.Require().NotNil(connB) + channelB = connB.FirstOrNextTestChannel() + + // ensure channel capability check passes + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"channel state is CLOSED", func() { + _, _, connA, connB, _, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + err := suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.Require().NoError(err) + }, false}, + {"connection not found", func() { + _, _, connA, connB, _, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + // set the channel's connection hops to wrong connection ID + channel := suite.chainB.GetChannel(channelB) + channel.ConnectionHops[0] = "doesnotexist" + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainB.GetContext(), channelB.PortID, channelB.ID, channel) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + + var err error + connB, connA, err = suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + + // create channel in init + channelB, _, err := suite.coordinator.ChanOpenInit(suite.chainB, suite.chainA, connB, connA, types.ORDERED) + suite.Require().NoError(err) + + // ensure channel capability check passes + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"consensus state not found", func() { + _, _, connA, connB, _, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + + heightDiff = 3 + }, false}, + {"channel verification failed", func() { + // channel not closed + _, _, connA, connB, _, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"channel capability not found", func() { + _, _, connA, connB, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + + channelCap = capabilitytypes.NewCapability(3) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + heightDiff = 0 // must explicitly be changed + + tc.malleate() + + channelA = connA.FirstOrNextTestChannel() + channelB = connB.FirstOrNextTestChannel() + + channelKey := host.KeyChannel(channelA.PortID, channelA.ID) + proof, proofHeight := suite.chainA.QueryProof(channelKey) + + err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanCloseConfirm( + suite.chainB.GetContext(), channelB.PortID, channelB.ID, channelCap, + proof, proofHeight+heightDiff, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/ibc/04-channel/keeper/keeper_test.go b/x/ibc/04-channel/keeper/keeper_test.go index 52cff3065..dc3e03b16 100644 --- a/x/ibc/04-channel/keeper/keeper_test.go +++ b/x/ibc/04-channel/keeper/keeper_test.go @@ -1,234 +1,250 @@ package keeper_test import ( - "fmt" "testing" - "time" "github.com/stretchr/testify/suite" - abci "github.com/tendermint/tendermint/abci/types" - lite "github.com/tendermint/tendermint/lite2" - tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" - ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" - commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" - host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" - ibckeeper "github.com/cosmos/cosmos-sdk/x/ibc/keeper" - - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// define constants used for testing -const ( - testClientIDA = "testclientida" - testConnectionIDA = "connectionidatob" - - testClientIDB = "testclientidb" - testConnectionIDB = "connectionidbtoa" - - testPort1 = "firstport" - testPort2 = "secondport" - testPort3 = "thirdport" - - testChannel1 = "firstchannel" - testChannel2 = "secondchannel" - testChannel3 = "thirdchannel" - - testChannelOrder = types.ORDERED - testChannelVersion = "1.0" - - trustingPeriod time.Duration = time.Hour * 24 * 7 * 2 - ubdPeriod time.Duration = time.Hour * 24 * 7 * 3 - maxClockDrift time.Duration = time.Second * 10 - - timeoutHeight = 100 - timeoutTimestamp = 100 - disabledTimeoutTimestamp = 0 - disabledTimeoutHeight = 0 -) - -var ( - testPacketCommitment = []byte("packet commitment") - testAcknowledgement = []byte("acknowledgement") + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) +// KeeperTestSuite is a testing suite to test keeper functions. type KeeperTestSuite struct { suite.Suite - cdc *codec.Codec - querier sdk.Querier + coordinator *ibctesting.Coordinator - chainA *TestChain - chainB *TestChain + // testing chains used for convience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain } +// TestKeeperTestSuite runs all the tests within this package. +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +// SetupTest creates a coordinator with 2 test chains. func (suite *KeeperTestSuite) SetupTest() { - suite.chainA = NewTestChain(testClientIDA) - suite.chainB = NewTestChain(testClientIDB) - - suite.cdc = suite.chainA.App.Codec() - suite.querier = ibckeeper.NewQuerier(*suite.chainA.App.IBCKeeper) + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) } +// TestSetChannel create clients and connections on both chains. It tests for the non-existence +// and existence of a channel in INIT on chainA. func (suite *KeeperTestSuite) TestSetChannel() { - ctx := suite.chainB.GetContext() - _, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetChannel(ctx, testPort1, testChannel1) + // create client and connections on both chains + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + + // check for channel to be created on chainB + channelA := connA.NextTestChannel() + _, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID) suite.False(found) - counterparty2 := types.NewCounterparty(testPort2, testChannel2) - channel := types.NewChannel( - types.INIT, testChannelOrder, - counterparty2, []string{testConnectionIDA}, testChannelVersion, - ) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel(ctx, testPort1, testChannel1, channel) + // init channel + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + suite.NoError(err) + + storedChannel, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID) + expectedCounterparty := types.NewCounterparty(channelB.PortID, channelB.ID) - storedChannel, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetChannel(ctx, testPort1, testChannel1) suite.True(found) - suite.Equal(channel, storedChannel) + suite.Equal(types.INIT, storedChannel.State) + suite.Equal(types.ORDERED, storedChannel.Ordering) + suite.Equal(expectedCounterparty, storedChannel.Counterparty) } +// TestGetAllChannels creates multiple channels on chain A through various connections +// and tests their retrieval. 2 channels are on connA0 and 1 channel is on connA1 func (suite KeeperTestSuite) TestGetAllChannels() { - // Channel (Counterparty): A(C) -> C(B) -> B(A) - counterparty1 := types.NewCounterparty(testPort1, testChannel1) - counterparty2 := types.NewCounterparty(testPort2, testChannel2) - counterparty3 := types.NewCounterparty(testPort3, testChannel3) + clientA, clientB, connA0, connB0, testchannel0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + // channel0 on first connection on chainA + counterparty0 := types.Counterparty{ + PortID: connB0.Channels[0].PortID, + ChannelID: connB0.Channels[0].ID, + } + // channel1 is second channel on first connection on chainA + testchannel1, _ := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA0, connB0, types.ORDERED) + counterparty1 := types.Counterparty{ + PortID: connB0.Channels[1].PortID, + ChannelID: connB0.Channels[1].ID, + } + + connA1, connB1 := suite.coordinator.CreateConnection(suite.chainA, suite.chainB, clientA, clientB) + + // channel2 is on a second connection on chainA + testchannel2, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA1, connB1, types.UNORDERED) + suite.Require().NoError(err) + + counterparty2 := types.Counterparty{ + PortID: connB1.Channels[0].PortID, + ChannelID: connB1.Channels[0].ID, + } + + channel0 := types.NewChannel( + types.OPEN, types.UNORDERED, + counterparty0, []string{connB0.ID}, ibctesting.ChannelVersion, + ) channel1 := types.NewChannel( - types.INIT, testChannelOrder, - counterparty3, []string{testConnectionIDA}, testChannelVersion, + types.OPEN, types.ORDERED, + counterparty1, []string{connB0.ID}, ibctesting.ChannelVersion, ) channel2 := types.NewChannel( - types.INIT, testChannelOrder, - counterparty1, []string{testConnectionIDA}, testChannelVersion, - ) - channel3 := types.NewChannel( - types.CLOSED, testChannelOrder, - counterparty2, []string{testConnectionIDA}, testChannelVersion, + types.INIT, types.UNORDERED, + counterparty2, []string{connB1.ID}, ibctesting.ChannelVersion, ) expChannels := []types.IdentifiedChannel{ - types.NewIdentifiedChannel(testPort1, testChannel1, channel1), - types.NewIdentifiedChannel(testPort2, testChannel2, channel2), - types.NewIdentifiedChannel(testPort3, testChannel3, channel3), + types.NewIdentifiedChannel(testchannel0.PortID, testchannel0.ID, channel0), + types.NewIdentifiedChannel(testchannel1.PortID, testchannel1.ID, channel1), + types.NewIdentifiedChannel(testchannel2.PortID, testchannel2.ID, channel2), } - ctx := suite.chainB.GetContext() + ctxA := suite.chainA.GetContext() - suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel(ctx, testPort1, testChannel1, channel1) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel(ctx, testPort2, testChannel2, channel2) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel(ctx, testPort3, testChannel3, channel3) - - channels := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllChannels(ctx) + channels := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllChannels(ctxA) suite.Require().Len(channels, len(expChannels)) suite.Require().Equal(expChannels, channels) } +// TestGetAllSequences sets all packet sequences for two different channels on chain A and +// tests their retrieval. func (suite KeeperTestSuite) TestGetAllSequences() { - seq1 := types.NewPacketSequence(testPort1, testChannel1, 1) - seq2 := types.NewPacketSequence(testPort2, testChannel2, 2) + _, _, connA, connB, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + channelA1, _ := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.UNORDERED) - expSeqs := []types.PacketSequence{seq1, seq2} + seq1 := types.NewPacketSequence(channelA0.PortID, channelA0.ID, 1) + seq2 := types.NewPacketSequence(channelA0.PortID, channelA0.ID, 2) + seq3 := types.NewPacketSequence(channelA1.PortID, channelA1.ID, 3) - ctx := suite.chainB.GetContext() + // seq1 should be overwritten by seq2 + expSeqs := []types.PacketSequence{seq2, seq3} - for _, seq := range expSeqs { - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(ctx, seq.PortID, seq.ChannelID, seq.Sequence) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(ctx, seq.PortID, seq.ChannelID, seq.Sequence) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(ctx, seq.PortID, seq.ChannelID, seq.Sequence) + ctxA := suite.chainA.GetContext() + + for _, seq := range []types.PacketSequence{seq1, seq2, seq3} { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(ctxA, seq.PortID, seq.ChannelID, seq.Sequence) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(ctxA, seq.PortID, seq.ChannelID, seq.Sequence) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(ctxA, seq.PortID, seq.ChannelID, seq.Sequence) } - sendSeqs := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketSendSeqs(ctx) - recvSeqs := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketRecvSeqs(ctx) - ackSeqs := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketAckSeqs(ctx) - suite.Require().Len(sendSeqs, 2) - suite.Require().Len(recvSeqs, 2) - suite.Require().Len(ackSeqs, 2) + sendSeqs := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketSendSeqs(ctxA) + recvSeqs := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketRecvSeqs(ctxA) + ackSeqs := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketAckSeqs(ctxA) + suite.Len(sendSeqs, 2) + suite.Len(recvSeqs, 2) + suite.Len(ackSeqs, 2) - suite.Require().Equal(expSeqs, sendSeqs) - suite.Require().Equal(expSeqs, recvSeqs) - suite.Require().Equal(expSeqs, ackSeqs) + suite.Equal(expSeqs, sendSeqs) + suite.Equal(expSeqs, recvSeqs) + suite.Equal(expSeqs, ackSeqs) } +// TestGetAllCommitmentsAcks creates a set of acks and packet commitments on two different +// channels on chain A and tests their retrieval. func (suite KeeperTestSuite) TestGetAllCommitmentsAcks() { - ack1 := types.NewPacketAckCommitment(testPort1, testChannel1, 1, []byte("ack")) - ack2 := types.NewPacketAckCommitment(testPort1, testChannel1, 2, []byte("ack")) - comm1 := types.NewPacketAckCommitment(testPort1, testChannel1, 1, []byte("hash")) - comm2 := types.NewPacketAckCommitment(testPort1, testChannel1, 2, []byte("hash")) + _, _, connA, connB, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + channelA1, _ := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.UNORDERED) - expAcks := []types.PacketAckCommitment{ack1, ack2} - expCommitments := []types.PacketAckCommitment{comm1, comm2} + // channel 0 acks + ack1 := types.NewPacketAckCommitment(channelA0.PortID, channelA0.ID, 1, []byte("ack")) + ack2 := types.NewPacketAckCommitment(channelA0.PortID, channelA0.ID, 2, []byte("ack")) - ctx := suite.chainB.GetContext() + // duplicate ack + ack2dup := types.NewPacketAckCommitment(channelA0.PortID, channelA0.ID, 2, []byte("ack")) - for i := 0; i < 2; i++ { - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctx, expAcks[i].PortID, expAcks[i].ChannelID, expAcks[i].Sequence, expAcks[i].Hash) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctx, expCommitments[i].PortID, expCommitments[i].ChannelID, expCommitments[i].Sequence, expCommitments[i].Hash) + // channel 1 acks + ack3 := types.NewPacketAckCommitment(channelA1.PortID, channelA1.ID, 1, []byte("ack")) + + // channel 0 packet commitments + comm1 := types.NewPacketAckCommitment(channelA0.PortID, channelA0.ID, 1, []byte("hash")) + comm2 := types.NewPacketAckCommitment(channelA0.PortID, channelA0.ID, 2, []byte("hash")) + + // channel 1 packet commitments + comm3 := types.NewPacketAckCommitment(channelA1.PortID, channelA1.ID, 1, []byte("hash")) + comm4 := types.NewPacketAckCommitment(channelA1.PortID, channelA1.ID, 2, []byte("hash")) + + expAcks := []types.PacketAckCommitment{ack1, ack2, ack3} + expCommitments := []types.PacketAckCommitment{comm1, comm2, comm3, comm4} + + ctxA := suite.chainA.GetContext() + + // set acknowledgements + for _, ack := range []types.PacketAckCommitment{ack1, ack2, ack2dup, ack3} { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctxA, ack.PortID, ack.ChannelID, ack.Sequence, ack.Hash) } - acks := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketAcks(ctx) - commitments := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketCommitments(ctx) - suite.Require().Len(acks, 2) - suite.Require().Len(commitments, 2) + // set packet commitments + for _, comm := range expCommitments { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctxA, comm.PortID, comm.ChannelID, comm.Sequence, comm.Hash) + } + + acks := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketAcks(ctxA) + commitments := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketCommitments(ctxA) + suite.Require().Len(acks, len(expAcks)) + suite.Require().Len(commitments, len(expCommitments)) suite.Require().Equal(expAcks, acks) suite.Require().Equal(expCommitments, commitments) } +// TestSetSequence verifies that the keeper correctly sets the sequence counters. func (suite *KeeperTestSuite) TestSetSequence() { - ctx := suite.chainB.GetContext() - _, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctx, testPort1, testChannel1) - suite.False(found) + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) - _, found = suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceRecv(ctx, testPort1, testChannel1) - suite.False(found) + ctxA := suite.chainA.GetContext() + one := uint64(1) - _, found = suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceAck(ctx, testPort1, testChannel1) - suite.False(found) + // initialized channel has next send seq of 1 + seq, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctxA, channelA.PortID, channelA.ID) + suite.True(found) + suite.Equal(one, seq) + + // initialized channel has next seq recv of 1 + seq, found = suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceRecv(ctxA, channelA.PortID, channelA.ID) + suite.True(found) + suite.Equal(one, seq) + + // initialized channel has next seq ack of + seq, found = suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceAck(ctxA, channelA.PortID, channelA.ID) + suite.True(found) + suite.Equal(one, seq) nextSeqSend, nextSeqRecv, nextSeqAck := uint64(10), uint64(10), uint64(10) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(ctx, testPort1, testChannel1, nextSeqSend) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(ctx, testPort1, testChannel1, nextSeqRecv) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(ctx, testPort1, testChannel1, nextSeqAck) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(ctxA, channelA.PortID, channelA.ID, nextSeqSend) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(ctxA, channelA.PortID, channelA.ID, nextSeqRecv) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(ctxA, channelA.PortID, channelA.ID, nextSeqAck) - storedNextSeqSend, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctx, testPort1, testChannel1) + storedNextSeqSend, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctxA, channelA.PortID, channelA.ID) suite.True(found) suite.Equal(nextSeqSend, storedNextSeqSend) - storedNextSeqRecv, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctx, testPort1, testChannel1) + storedNextSeqRecv, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctxA, channelA.PortID, channelA.ID) suite.True(found) suite.Equal(nextSeqRecv, storedNextSeqRecv) - storedNextSeqAck, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceAck(ctx, testPort1, testChannel1) + storedNextSeqAck, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceAck(ctxA, channelA.PortID, channelA.ID) suite.True(found) suite.Equal(nextSeqAck, storedNextSeqAck) } -// TestPacketCommitment does basic verification of setting and getting of packet commitments within -// the Channel Keeper. -func (suite *KeeperTestSuite) TestPacketCommitment() { - ctx := suite.chainB.GetContext() - seq := uint64(10) - - storedCommitment := suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, testPort1, testChannel1, seq) - suite.Nil(storedCommitment) - - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctx, testPort1, testChannel1, seq, testPacketCommitment) - - storedCommitment = suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, testPort1, testChannel1, seq) - suite.Equal(testPacketCommitment, storedCommitment) -} - -// TestGetAllPacketCommitmentsAtChannel verifies that iterator returns all stored packet commitments -// for a specific channel. +// TestGetAllPacketCommitmentsAtChannel verifies that the keeper returns all stored packet +// commitments for a specific channel. The test will store consecutive commitments up to the +// value of "seq" and then add non-consecutive up to the value of "maxSeq". A final commitment +// with the value maxSeq + 1 is set on a different channel. func (suite *KeeperTestSuite) TestGetAllPacketCommitmentsAtChannel() { - // setup - ctx := suite.chainB.GetContext() + _, _, connA, connB, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + + // create second channel + channelA1, _ := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.UNORDERED) + + ctxA := suite.chainA.GetContext() expectedSeqs := make(map[uint64]bool) + hash := []byte("commitment") seq := uint64(15) maxSeq := uint64(25) @@ -236,289 +252,53 @@ func (suite *KeeperTestSuite) TestGetAllPacketCommitmentsAtChannel() { // create consecutive commitments for i := uint64(1); i < seq; i++ { - suite.chainB.storePacketCommitment(ctx, testPort1, testChannel1, i) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctxA, channelA.PortID, channelA.ID, i, hash) expectedSeqs[i] = true } // add non-consecutive commitments for i := seq; i < maxSeq; i += 2 { - suite.chainB.storePacketCommitment(ctx, testPort1, testChannel1, i) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctxA, channelA.PortID, channelA.ID, i, hash) expectedSeqs[i] = true } // add sequence on different channel/port - suite.chainB.storePacketCommitment(ctx, testPort2, testChannel2, maxSeq+1) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctxA, channelA1.PortID, channelA1.ID, maxSeq+1, hash) - commitments := suite.chainB.App.IBCKeeper.ChannelKeeper.GetAllPacketCommitmentsAtChannel(ctx, testPort1, testChannel1) + commitments := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketCommitmentsAtChannel(ctxA, channelA.PortID, channelA.ID) suite.Equal(len(expectedSeqs), len(commitments)) + // ensure above for loops occurred suite.NotEqual(0, len(commitments)) // verify that all the packet commitments were stored for _, packet := range commitments { suite.True(expectedSeqs[packet.Sequence]) - suite.Equal(testPort1, packet.PortID) - suite.Equal(testChannel1, packet.ChannelID) + suite.Equal(channelA.PortID, packet.PortID) + suite.Equal(channelA.ID, packet.ChannelID) + suite.Equal(hash, packet.Hash) // prevent duplicates from passing checks expectedSeqs[packet.Sequence] = false } } +// TestSetPacketAcknowledgement verifies that packet acknowledgements are correctly +// set in the keeper. func (suite *KeeperTestSuite) TestSetPacketAcknowledgement() { - ctx := suite.chainB.GetContext() + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + + ctxA := suite.chainA.GetContext() seq := uint64(10) - storedAckHash, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(ctx, testPort1, testChannel1, seq) + storedAckHash, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(ctxA, channelA.PortID, channelA.ID, seq) suite.False(found) suite.Nil(storedAckHash) ackHash := []byte("ackhash") - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctx, testPort1, testChannel1, seq, ackHash) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctxA, channelA.PortID, channelA.ID, seq, ackHash) - storedAckHash, found = suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(ctx, testPort1, testChannel1, seq) + storedAckHash, found = suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(ctxA, channelA.PortID, channelA.ID, seq) suite.True(found) suite.Equal(ackHash, storedAckHash) } - -func TestKeeperTestSuite(t *testing.T) { - suite.Run(t, new(KeeperTestSuite)) -} - -func commitNBlocks(chain *TestChain, n int) { - for i := 0; i < n; i++ { - chain.App.Commit() - chain.App.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: chain.App.LastBlockHeight() + 1}}) - } -} - -// commit current block and start the next block with the provided time -func commitBlockWithNewTimestamp(chain *TestChain, timestamp int64) { - chain.App.Commit() - chain.App.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: chain.App.LastBlockHeight() + 1, Time: time.Unix(timestamp, 0)}}) -} - -// nolint: unused -func queryProof(chain *TestChain, key []byte) ([]byte, uint64) { - res := chain.App.Query(abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", host.StoreKey), - Height: chain.App.LastBlockHeight(), - Data: key, - Prove: true, - }) - - merkleProof := commitmenttypes.MerkleProof{ - Proof: res.Proof, - } - - proof, _ := chain.App.AppCodec().MarshalBinaryBare(&merkleProof) - - return proof, uint64(res.Height) -} - -type TestChain struct { - ClientID string - App *simapp.SimApp - Header ibctmtypes.Header - Vals *tmtypes.ValidatorSet - Signers []tmtypes.PrivValidator -} - -func NewTestChain(clientID string) *TestChain { - privVal := tmtypes.NewMockPV() - - pubKey, err := privVal.GetPubKey() - if err != nil { - panic(err) - } - - validator := tmtypes.NewValidator(pubKey, 1) - valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - signers := []tmtypes.PrivValidator{privVal} - now := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) - - header := ibctmtypes.CreateTestHeader(clientID, 1, now, valSet, signers) - - return &TestChain{ - ClientID: clientID, - App: simapp.Setup(false), - Header: header, - Vals: valSet, - Signers: signers, - } -} - -// Creates simple context for testing purposes -func (chain *TestChain) GetContext() sdk.Context { - return chain.App.BaseApp.NewContext(false, abci.Header{ChainID: chain.Header.SignedHeader.Header.ChainID, Height: int64(chain.Header.GetHeight())}) -} - -// createClient will create a client for clientChain on targetChain -func (chain *TestChain) CreateClient(client *TestChain) error { - client.Header = nextHeader(client) - // Commit and create a new block on appTarget to get a fresh CommitID - client.App.Commit() - commitID := client.App.LastCommitID() - client.App.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: int64(client.Header.GetHeight()), Time: client.Header.Time}}) - - // Set HistoricalInfo on client chain after Commit - ctxClient := client.GetContext() - validator := stakingtypes.NewValidator( - sdk.ValAddress(client.Vals.Validators[0].Address), client.Vals.Validators[0].PubKey, stakingtypes.Description{}, - ) - validator.Status = sdk.Bonded - validator.Tokens = sdk.NewInt(1000000) // get one voting power - validators := []stakingtypes.Validator{validator} - histInfo := stakingtypes.HistoricalInfo{ - Header: abci.Header{ - AppHash: commitID.Hash, - }, - Valset: validators, - } - client.App.StakingKeeper.SetHistoricalInfo(ctxClient, int64(client.Header.GetHeight()), histInfo) - - // Create target ctx - ctxTarget := chain.GetContext() - - // create client - clientState, err := ibctmtypes.Initialize(client.ClientID, lite.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, client.Header, commitmenttypes.GetSDKSpecs()) - if err != nil { - return err - } - _, err = chain.App.IBCKeeper.ClientKeeper.CreateClient(ctxTarget, clientState, client.Header.ConsensusState()) - if err != nil { - return err - } - return nil - - // _, _, err := simapp.SignCheckDeliver( - // suite.T(), - // suite.cdc, - // suite.app.BaseApp, - // ctx.BlockHeader(), - // []sdk.Msg{clienttypes.NewMsgCreateClient(clientID, clientexported.ClientTypeTendermint, consState, accountAddress)}, - // []uint64{baseAccount.GetAccountNumber()}, - // []uint64{baseAccount.GetSequence()}, - // true, true, accountPrivKey, - // ) -} - -func (chain *TestChain) updateClient(client *TestChain) { - // Create target ctx - ctxTarget := chain.GetContext() - - // if clientState does not already exist, return without updating - _, found := chain.App.IBCKeeper.ClientKeeper.GetClientState( - ctxTarget, client.ClientID, - ) - if !found { - return - } - - // always commit when updateClient and begin a new block - client.App.Commit() - commitID := client.App.LastCommitID() - - client.Header = nextHeader(client) - client.App.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: int64(client.Header.GetHeight()), Time: client.Header.Time}}) - - // Set HistoricalInfo on client chain after Commit - ctxClient := client.GetContext() - validator := stakingtypes.NewValidator( - sdk.ValAddress(client.Vals.Validators[0].Address), client.Vals.Validators[0].PubKey, stakingtypes.Description{}, - ) - validator.Status = sdk.Bonded - validator.Tokens = sdk.NewInt(1000000) - validators := []stakingtypes.Validator{validator} - histInfo := stakingtypes.HistoricalInfo{ - Header: abci.Header{ - AppHash: commitID.Hash, - }, - Valset: validators, - } - client.App.StakingKeeper.SetHistoricalInfo(ctxClient, int64(client.Header.GetHeight()), histInfo) - - consensusState := ibctmtypes.ConsensusState{ - Height: client.Header.GetHeight(), - Timestamp: client.Header.Time, - Root: commitmenttypes.NewMerkleRoot(commitID.Hash), - ValidatorSet: client.Vals, - } - - chain.App.IBCKeeper.ClientKeeper.SetClientConsensusState( - ctxTarget, client.ClientID, client.Header.GetHeight(), consensusState, - ) - chain.App.IBCKeeper.ClientKeeper.SetClientState( - ctxTarget, ibctmtypes.NewClientState(client.ClientID, lite.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, client.Header, commitmenttypes.GetSDKSpecs()), - ) - - // _, _, err := simapp.SignCheckDeliver( - // suite.T(), - // suite.cdc, - // suite.app.BaseApp, - // ctx.BlockHeader(), - // []sdk.Msg{clienttypes.NewMsgUpdateClient(clientID, suite.header, accountAddress)}, - // []uint64{baseAccount.GetAccountNumber()}, - // []uint64{baseAccount.GetSequence()}, - // true, true, accountPrivKey, - // ) - // suite.Require().NoError(err) -} - -func (chain *TestChain) createConnection( - connID, counterpartyConnID, clientID, counterpartyClientID string, - state connectiontypes.State, -) connectiontypes.ConnectionEnd { - counterparty := connectiontypes.NewCounterparty(counterpartyClientID, counterpartyConnID, commitmenttypes.NewMerklePrefix(chain.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix().Bytes())) - connection := connectiontypes.ConnectionEnd{ - State: state, - ClientID: clientID, - Counterparty: counterparty, - Versions: connectiontypes.GetCompatibleVersions(), - } - ctx := chain.GetContext() - chain.App.IBCKeeper.ConnectionKeeper.SetConnection(ctx, connID, connection) - return connection -} - -func (chain *TestChain) createChannel( - portID, channelID, counterpartyPortID, counterpartyChannelID string, - state types.State, order types.Order, connectionID string, -) types.Channel { - counterparty := types.NewCounterparty(counterpartyPortID, counterpartyChannelID) - - // sets channel with given state - channel := types.NewChannel(state, order, counterparty, - []string{connectionID}, testChannelVersion, - ) - ctx := chain.GetContext() - chain.App.IBCKeeper.ChannelKeeper.SetChannel(ctx, portID, channelID, channel) - return channel -} - -// storePacketCommitment is a helper function that sets a packet commitment in the Channel Keeper. -func (chain *TestChain) storePacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) { - chain.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctx, portID, channelID, sequence, testPacketCommitment) -} - -// storeAcknowledgement is a helper function that sets a packet commitment in the Channel Keeper. -func (chain *TestChain) storeAcknowledgement(ctx sdk.Context, portID, channelID string, sequence uint64) { - chain.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctx, portID, channelID, sequence, testAcknowledgement) -} - -func nextHeader(chain *TestChain) ibctmtypes.Header { - return ibctmtypes.CreateTestHeader(chain.Header.SignedHeader.Header.ChainID, int64(chain.Header.GetHeight())+1, - chain.Header.Time.Add(time.Minute), chain.Vals, chain.Signers) -} - -// Mocked types - -type mockSuccessPacket struct{} - -// GetBytes returns the serialised packet data -func (mp mockSuccessPacket) GetBytes() []byte { return []byte("THIS IS A SUCCESS PACKET") } - -type mockFailPacket struct{} - -// GetBytes returns the serialised packet data (without timeout) -func (mp mockFailPacket) GetBytes() []byte { return []byte("THIS IS A FAILURE PACKET") } diff --git a/x/ibc/04-channel/keeper/packet_test.go b/x/ibc/04-channel/keeper/packet_test.go index fa4399617..571829063 100644 --- a/x/ibc/04-channel/keeper/packet_test.go +++ b/x/ibc/04-channel/keeper/packet_test.go @@ -4,92 +4,175 @@ import ( "fmt" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - transfertypes "github.com/cosmos/cosmos-sdk/x/ibc-transfer/types" - connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) -func (suite *KeeperTestSuite) TestSendPacket() { - counterparty := types.NewCounterparty(testPort2, testChannel2) - var packet exported.PacketI +var ( + validPacketData = []byte("VALID PACKET DATA") + disabledTimeoutTimestamp = uint64(0) + disabledTimeoutHeight = uint64(0) + timeoutHeight = uint64(100) + + // for when the testing package cannot be used + clientIDA = "clientA" + clientIDB = "clientB" + connIDA = "connA" + connIDB = "connB" + portID = "portid" + channelIDA = "channelidA" + channelIDB = "channelidB" +) + +// TestSendPacket tests SendPacket from chainA to chainB +func (suite *KeeperTestSuite) TestSendPacket() { + var ( + packet exported.PacketI + channelCap *capabilitytypes.Capability + ) - var channelCap *capabilitytypes.Capability testCases := []testCase{ - {"success", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainB.GetContext(), testPort1, testChannel1, 1) + {"success: UNORDERED channel", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, true}, - {"packet basic validation failed", func() { - packet = types.NewPacket(mockFailPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) + {"success: ORDERED channel", func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"sending packet out of order on UNORDERED channel", func() { + // setup creates an unordered channel + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 5, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"sending packet out of order on ORDERED channel", func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 5, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet basic validation failed, empty packet data", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket([]byte{}, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"channel not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) + // use wrong channel naming + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"channel closed", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.CLOSED, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) }, false}, {"packet dest port ≠ channel counterparty port", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, testPort3, counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"packet dest channel ID ≠ channel counterparty channel ID", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), testChannel3, timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong channel for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"connection not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, ibctesting.ChannelVersion), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"connection is UNINITIALIZED", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.UNINITIALIZED) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + // set connection as UNINITIALIZED + counterparty := connectiontypes.NewCounterparty(clientIDB, connIDA, suite.chainB.GetPrefix()) + connection := connectiontypes.NewConnectionEnd(connectiontypes.UNINITIALIZED, clientIDA, connIDA, counterparty, []string{ibctesting.ConnectionVersion}) + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connIDA, connection) + + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, ibctesting.ChannelVersion), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"client state not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + + // change connection client ID + connection := suite.chainA.GetConnection(connA) + connection.ClientID = ibctesting.InvalidID + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, connection) + + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"timeout height passed", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - commitNBlocks(suite.chainB, 10) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + clientA, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use client state latest height for timeout + clientState := suite.chainA.GetClientState(clientA) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clientState.GetLatestHeight(), disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"timeout timestamp passed", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), disabledTimeoutHeight, timeoutTimestamp) - commitBlockWithNewTimestamp(suite.chainB, timeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + clientA, _, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use latest time on client state + clientState := suite.chainA.GetClientState(clientA) + connection := suite.chainA.GetConnection(connA) + timestamp, err := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetTimestampAtHeight(suite.chainA.GetContext(), connection, clientState.GetLatestHeight()) + suite.Require().NoError(err) + + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, disabledTimeoutHeight, timestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"next sequence send not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA := connA.NextTestChannel() + channelB := connB.NextTestChannel() + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // manually creating channel prevents next sequence from being set + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connA.ID}, ibctesting.ChannelVersion), + ) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"next sequence wrong", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainB.GetContext(), testPort1, testChannel1, 5) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 5) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"channel capability not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainB.GetContext(), testPort1, testChannel1, 1) - channelCap = capabilitytypes.NewCapability(3) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = capabilitytypes.NewCapability(5) }, false}, } @@ -98,13 +181,9 @@ func (suite *KeeperTestSuite) TestSendPacket() { suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { suite.SetupTest() // reset - var err error - channelCap, err = suite.chainB.App.ScopedIBCKeeper.NewCapability(suite.chainB.GetContext(), host.ChannelCapabilityPath(testPort1, testChannel1)) - suite.Require().Nil(err, "could not create capability") - tc.malleate() - err = suite.chainB.App.IBCKeeper.ChannelKeeper.SendPacket(suite.chainB.GetContext(), channelCap, packet) + err := suite.chainA.App.IBCKeeper.ChannelKeeper.SendPacket(suite.chainA.GetContext(), channelCap, packet) if tc.expPass { suite.Require().NoError(err) @@ -116,81 +195,128 @@ func (suite *KeeperTestSuite) TestSendPacket() { } +// TestRecvPacket test RecvPacket on chainB. Since packet commitment verification will always +// occur last (resource instensive), only tests expected to succeed and packet commitment +// verification tests need to simulate sending a packet from chainA to chainB. func (suite *KeeperTestSuite) TestRecvPacket() { - counterparty := types.NewCounterparty(testPort1, testChannel1) - packetKey := host.KeyPacketCommitment(testPort2, testChannel2, 1) - - var packet exported.PacketI + var ( + packet exported.PacketI + ) testCases := []testCase{ - {"success ordered channel", func() { - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDB) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), testPort2, testChannel2, 1) - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitPacket(packet)) + {"success: ORDERED channel", func() { + _, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) }, true}, - {"success unordered channel", func() { - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.UNORDERED, testConnectionIDB) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), testPort2, testChannel2, 1) - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitPacket(packet)) + {"success UNORDERED channel", func() { + // setup uses an UNORDERED channel + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) }, true}, - {"channel not found", func() {}, false}, + {"success with out of order packet: UNORDERED channel", func() { + // setup uses an UNORDERED channel + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // send 2 packets + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + // set sequence to 2 + packet = types.NewPacket(validPacketData, 2, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + err = suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + // attempts to receive packet 2 without receiving packet 1 + }, true}, + // TODO: uncomment with implementation of issue: https://github.com/cosmos/cosmos-sdk/issues/6519 + /* {"out of order packet failure with ORDERED channel", func() { + _, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // send 2 packets + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + // set sequence to 2 + packet = types.NewPacket(validPacketData, 2, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + err = suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + // attempts to receive packet 2 without receiving packet 1 + }, false}, */ + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, {"channel not open", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.CLOSED, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.Require().NoError(err) }, false}, {"packet source port ≠ channel counterparty port", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort3, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"packet source channel ID ≠ channel counterparty channel ID", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel3, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"connection not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainB.GetContext(), + channelB.PortID, channelB.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelA.PortID, channelA.ID), []string{connIDB}, ibctesting.ChannelVersion), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"connection not OPEN", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.INIT) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + // connection on chainB is in INIT + connB, connA, err := suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + + channelA := connA.NextTestChannel() + channelB := connB.NextTestChannel() + // pass channel check + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainB.GetContext(), + channelB.PortID, channelB.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelA.PortID, channelA.ID), []string{connB.ID}, ibctesting.ChannelVersion), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"timeout height passed", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), 1, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp) }, false}, {"timeout timestamp passed", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, 1) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, disabledTimeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) }, false}, {"acknowledgement already received", func() { - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.UNORDERED, testConnectionIDB) - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.storeAcknowledgement(suite.chainB.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + // setup uses an UNORDERED channel + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + + // write packet acknowledgement + suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) }, false}, {"validation failed", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort2, testChannel2, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + // packet commitment not set resulting in invalid proof + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, } @@ -200,18 +326,15 @@ func (suite *KeeperTestSuite) TestRecvPacket() { suite.SetupTest() // reset tc.malleate() - ctx := suite.chainB.GetContext() + // get proof of packet commitment from chainA + packetKey := host.KeyPacketCommitment(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainA.QueryProof(packetKey) - suite.chainB.updateClient(suite.chainA) - suite.chainA.updateClient(suite.chainB) - proof, proofHeight := queryProof(suite.chainA, packetKey) + _, err := suite.chainB.App.IBCKeeper.ChannelKeeper.RecvPacket(suite.chainB.GetContext(), packet, proof, proofHeight) - var err error if tc.expPass { - _, err = suite.chainB.App.IBCKeeper.ChannelKeeper.RecvPacket(ctx, packet, proof, proofHeight+1) suite.Require().NoError(err) } else { - packet, err = suite.chainB.App.IBCKeeper.ChannelKeeper.RecvPacket(ctx, packet, proof, proofHeight) suite.Require().Error(err) } }) @@ -219,40 +342,72 @@ func (suite *KeeperTestSuite) TestRecvPacket() { } +// TestPacketExecuted tests the PacketExecuted call on chainB. func (suite *KeeperTestSuite) TestPacketExecuted() { - counterparty := types.NewCounterparty(testPort2, testChannel2) - var packet types.Packet + var ( + packet types.Packet + channelCap *capabilitytypes.Capability + ) - var channelCap *capabilitytypes.Capability testCases := []testCase{ {"success: UNORDERED", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainA.GetContext(), testPort2, testChannel2, 1) + // setup uses an UNORDERED channel + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) }, true}, {"success: ORDERED", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainA.GetContext(), testPort2, testChannel2, 1) + _, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) }, true}, - {"channel not found", func() {}, false}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, {"channel not OPEN", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.CLOSED, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.Require().NoError(err) }, false}, {"next sequence receive not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA := connA.NextTestChannel() + channelB := connB.NextTestChannel() + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // manually creating channel prevents next sequence receive from being set + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainB.GetContext(), + channelB.PortID, channelB.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelA.PortID, channelA.ID), []string{connB.ID}, ibctesting.ChannelVersion), + ) + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) }, false}, {"packet sequence ≠ next sequence receive", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainA.GetContext(), testPort2, testChannel2, 5) + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + // increments sequence receive + err := suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) }, false}, {"capability not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainA.GetContext(), testPort2, testChannel2, 1) + _, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + channelCap = capabilitytypes.NewCapability(3) }, false}, } @@ -262,16 +417,16 @@ func (suite *KeeperTestSuite) TestPacketExecuted() { suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { suite.SetupTest() // reset - var err error - channelCap, err = suite.chainA.App.ScopedIBCKeeper.NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(testPort2, testChannel2)) - suite.Require().NoError(err, "could not create capability") - tc.malleate() - err = suite.chainA.App.IBCKeeper.ChannelKeeper.PacketExecuted(suite.chainA.GetContext(), channelCap, packet, mockSuccessPacket{}.GetBytes()) + ack := ibctesting.TestHash + err := suite.chainB.App.IBCKeeper.ChannelKeeper.PacketExecuted(suite.chainB.GetContext(), channelCap, packet, ack) if tc.expPass { suite.Require().NoError(err) + // verify packet ack is written + actualAck := suite.chainB.GetAcknowledgement(packet) + suite.Require().Equal(types.CommitAcknowledgement(ack), actualAck) } else { suite.Require().Error(err) } @@ -279,96 +434,132 @@ func (suite *KeeperTestSuite) TestPacketExecuted() { } } +// TestAcknowledgePacket tests the call AcknowledgePacket on chainA. func (suite *KeeperTestSuite) TestAcknowledgePacket() { - counterparty := types.NewCounterparty(testPort2, testChannel2) - var packet types.Packet - packetKey := host.KeyPacketAcknowledgement(testPort2, testChannel2, 1) - - ack := transfertypes.FungibleTokenPacketAcknowledgement{ - Success: true, - }.GetBytes() + var ( + packet types.Packet + ack = ibctesting.TestHash + ) testCases := []testCase{ {"success on ordered channel", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDB) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitAcknowledgement(ack)) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(suite.chainB.GetContext(), counterparty.GetPortID(), counterparty.GetChannelID(), 1) + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet acknowledgement + err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) }, true}, {"success on unordered channel", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.UNORDERED, testConnectionIDB) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitAcknowledgement(ack)) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(suite.chainB.GetContext(), counterparty.GetPortID(), counterparty.GetChannelID(), 1) - }, true}, + // setup uses an UNORDERED channel + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) - {"channel not found", func() {}, false}, + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet acknowledgement + err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) + }, true}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, {"channel not open", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.CLOSED, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) }, false}, - {"packet source port ≠ channel counterparty port", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort3, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + {"packet destination port ≠ channel counterparty port", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, - {"packet source channel ID ≠ channel counterparty channel ID", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel3, types.OPEN, types.ORDERED, testConnectionIDA) + {"packet destination channel ID ≠ channel counterparty channel ID", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong channel for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"connection not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainB.GetContext(), + channelB.PortID, channelB.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelA.PortID, channelA.ID), []string{connIDB}, ibctesting.ChannelVersion), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"connection not OPEN", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.INIT) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + // connection on chainA is in INIT + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + channelA := connA.NextTestChannel() + channelB := connB.NextTestChannel() + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connA.ID}, ibctesting.ChannelVersion), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"packet hasn't been sent", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + // packet commitment never written + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"packet ack verification failed", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) + // ack never written + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create packet commitment + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) }, false}, {"next ack sequence not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDB) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitAcknowledgement(ack)) + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA := connA.NextTestChannel() + channelB := connB.NextTestChannel() + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // manually creating channel prevents next sequence acknowledgement from being set + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connA.ID}, ibctesting.ChannelVersion), + ) + // manually set packet commitment + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), channelA.PortID, channelA.ID, packet.GetSequence(), ibctesting.TestHash) + + // manually set packet acknowledgement + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), channelB.PortID, channelB.ID, packet.GetSequence(), ibctesting.TestHash) }, false}, {"next ack sequence mismatch", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDB) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitAcknowledgement(ack)) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(suite.chainB.GetContext(), counterparty.GetPortID(), counterparty.GetChannelID(), 10) + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet acknowledgement + err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) + + // set next sequence ack wrong + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 10) }, false}, } @@ -378,30 +569,23 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { suite.SetupTest() // reset tc.malleate() - suite.chainA.updateClient(suite.chainB) - suite.chainB.updateClient(suite.chainA) - proof, proofHeight := queryProof(suite.chainA, packetKey) + packetKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainB.QueryProof(packetKey) - ctx := suite.chainB.GetContext() - packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.AcknowledgePacket(ctx, packet, ack, proof, proofHeight+1) + _, err := suite.chainA.App.IBCKeeper.ChannelKeeper.AcknowledgePacket(suite.chainA.GetContext(), packet, ack, proof, proofHeight) if tc.expPass { suite.Require().NoError(err) - suite.Require().NotNil(packetOut) } else { suite.Require().Error(err) - suite.Require().Nil(packetOut) } }) } } -// TestAcknowledgementExectued verifies that packet commitments are deleted after -// capabilities are verified. +// TestAcknowledgementExectued verifies that packet commitments are deleted on chainA +// after capabilities are verified. func (suite *KeeperTestSuite) TestAcknowledgementExecuted() { - sequence := uint64(1) - counterparty := types.NewCounterparty(testPort2, testChannel2) - var ( packet types.Packet chanCap *capabilitytypes.Capability @@ -409,14 +593,29 @@ func (suite *KeeperTestSuite) TestAcknowledgementExecuted() { testCases := []testCase{ {"success ORDERED", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), sequence, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), sequence, types.CommitPacket(packet)) + // setup uses an UNORDERED channel + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet acknowledgement + err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) + + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, true}, - {"channel not found", func() {}, false}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, {"incorrect capability", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), sequence, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + chanCap = capabilitytypes.NewCapability(100) }, false}, } @@ -426,15 +625,9 @@ func (suite *KeeperTestSuite) TestAcknowledgementExecuted() { suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { suite.SetupTest() // reset - var err error - chanCap, err = suite.chainA.App.ScopedIBCKeeper.NewCapability( - suite.chainA.GetContext(), host.ChannelCapabilityPath(testPort1, testChannel1), - ) - suite.Require().NoError(err, "could not create capability") - tc.malleate() - err = suite.chainA.App.IBCKeeper.ChannelKeeper.AcknowledgementExecuted(suite.chainA.GetContext(), chanCap, packet) + err := suite.chainA.App.IBCKeeper.ChannelKeeper.AcknowledgementExecuted(suite.chainA.GetContext(), chanCap, packet) pc := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if tc.expPass { @@ -446,142 +639,223 @@ func (suite *KeeperTestSuite) TestAcknowledgementExecuted() { }) } } -func (suite *KeeperTestSuite) TestCleanupPacket() { - counterparty := types.NewCounterparty(testPort2, testChannel2) - unorderedPacketKey := host.KeyPacketAcknowledgement(testPort2, testChannel2, 1) - orderedPacketKey := host.KeyNextSequenceRecv(testPort2, testChannel2) +// TestCleanupPacket tests calling CleanupPacket on chainA. Each test case must specifiy the +// channel ordering so the appropriate proof can be created. It must also specifiy the next +// sequence receive so that correct amount of deleted packet commitments can be checked for. +// The last sent/received packet and the channel capability are expected to be defined. +func (suite *KeeperTestSuite) TestCleanupPacket() { var ( packet types.Packet nextSeqRecv uint64 ordered bool + channelCap *capabilitytypes.Capability + ack = ibctesting.TestHash ) - ack := []byte("ack") - testCases := []testCase{ - {"success on ordered channel", func() { + {"success: ORDERED channel", func() { ordered = true nextSeqRecv = 6 - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.ORDERED, testConnectionIDB) - // create several packet commitments + + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + + // create several send and receives for i := uint64(1); i < nextSeqRecv; i++ { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), i, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, i, types.CommitPacket(packet)) + packet = types.NewPacket(validPacketData, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet acknowledgement + err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) } - // set next sequence recv - suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainA.GetContext(), testPort2, testChannel2, nextSeqRecv) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, true}, - {"success on unordered channel", func() { + {"success: UNORDERED channel", func() { ordered = false - nextSeqRecv = 10 - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.UNORDERED, testConnectionIDB) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), testPort2, testChannel2, 1, types.CommitAcknowledgement(ack)) + nextSeqRecv = 5 + + // setup uses an UNORDERED channel + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + + for i := uint64(1); i < nextSeqRecv; i++ { + packet = types.NewPacket(validPacketData, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet acknowledgement + err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) + } + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, true}, - {"channel not found", func() {}, false}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, {"channel not open", func() { - ordered = true - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.CLOSED, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) }, false}, - {"packet source port ≠ channel counterparty port", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort3, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + {"packet destination port ≠ channel counterparty port", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, - {"packet source channel ID ≠ channel counterparty channel ID", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel3, types.OPEN, types.ORDERED, testConnectionIDA) + {"packet destination channel ID ≠ channel counterparty channel ID", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong channel for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"connection not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, ibctesting.ChannelVersion), + ) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"connection not OPEN", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.INIT) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, clientexported.Tendermint) + // connection on chainA is in INIT + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + channelA := connA.NextTestChannel() + channelB := connB.NextTestChannel() + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connA.ID}, ibctesting.ChannelVersion), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, - {"packet already received ", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 10, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + {"packet hasn't been received", func() { + ordered = true + nextSeqRecv = 6 + + _, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + + for i := uint64(1); i < nextSeqRecv; i++ { + packet = types.NewPacket(validPacketData, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + } + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"packet hasn't been sent", func() { - nextSeqRecv = 10 - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - }, false}, - {"next seq receive verification failed", func() { - nextSeqRecv = 10 - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) - }, false}, - {"packet ack verification failed", func() { - nextSeqRecv = 10 ordered = false - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 1, types.CommitPacket(packet)) + nextSeqRecv = 5 + + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + + packet = types.NewPacket(validPacketData, nextSeqRecv-1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"next seq receive verification failed: ORDERED channel", func() { + // set ordered to false giving wrong proof + ordered = false + nextSeqRecv = 2 + + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + + // create several send and receives + for i := uint64(1); i < nextSeqRecv; i++ { + packet = types.NewPacket(validPacketData, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet acknowledgement + err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) + } + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet ack verification failed: UNORDERED channel", func() { + // set ordered to true giving wrong proof + ordered = true + nextSeqRecv = 5 + + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + + for i := uint64(1); i < nextSeqRecv; i++ { + packet = types.NewPacket(validPacketData, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet acknowledgement + err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) + } + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, } for i, tc := range testCases { tc := tc suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { - var proof []byte - var proofHeight uint64 + var ( + proof []byte + proofHeight uint64 + ) suite.SetupTest() // reset tc.malleate() - ctx := suite.chainB.GetContext() - - suite.chainB.updateClient(suite.chainA) - suite.chainA.updateClient(suite.chainB) + orderedPacketKey := host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel()) + unorderedPacketKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) if ordered { - proof, proofHeight = queryProof(suite.chainA, orderedPacketKey) + proof, proofHeight = suite.chainB.QueryProof(orderedPacketKey) } else { - proof, proofHeight = queryProof(suite.chainA, unorderedPacketKey) + proof, proofHeight = suite.chainB.QueryProof(unorderedPacketKey) } - cap, err := suite.chainB.App.ScopedIBCKeeper.NewCapability(ctx, host.ChannelCapabilityPath(testPort1, testChannel1)) - suite.Require().NoError(err) + _, err := suite.chainA.App.IBCKeeper.ChannelKeeper.CleanupPacket(suite.chainA.GetContext(), channelCap, packet, proof, proofHeight, nextSeqRecv, ack) if tc.expPass { - packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.CleanupPacket(ctx, cap, packet, proof, proofHeight+1, nextSeqRecv, ack) suite.Require().NoError(err) - suite.Require().NotNil(packetOut) if ordered { for i := uint64(1); i < nextSeqRecv; i++ { - pc := suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, testPort1, testChannel1, i) + pc := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), i) suite.Require().Nil(pc) } } else { - pc := suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, testPort1, testChannel1, packet.GetSequence()) + pc := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(suite.chainA.GetContext(), packet.GetSourceChannel(), packet.GetSourceChannel(), packet.GetSequence()) suite.Require().Nil(pc) } } else { - packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.CleanupPacket(ctx, cap, packet, proof, proofHeight, nextSeqRecv, ack) suite.Require().Error(err) - suite.Require().Nil(packetOut) } }) } diff --git a/x/ibc/04-channel/keeper/querier_test.go b/x/ibc/04-channel/keeper/querier_test.go index 565e4ff5a..d07422c37 100644 --- a/x/ibc/04-channel/keeper/querier_test.go +++ b/x/ibc/04-channel/keeper/querier_test.go @@ -4,7 +4,8 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/codec" - connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" ) @@ -18,7 +19,7 @@ func (suite *KeeperTestSuite) TestQueryChannels() { ) params := types.NewQueryAllChannelsParams(1, 100) - data, err := suite.cdc.MarshalJSON(params) + data, err := suite.chainA.App.AppCodec().MarshalJSON(params) suite.Require().NoError(err) query := abci.RequestQuery{ @@ -33,88 +34,83 @@ func (suite *KeeperTestSuite) TestQueryChannels() { { "success with different connection channels", func() { - suite.SetupTest() channels := make([]types.IdentifiedChannel, 0, 2) - // create channels on different connections - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, - testClientIDA, testClientIDB, - connection.OPEN, - ) + // create first connection/channel + clientA, clientB, _, _, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + channels = append(channels, - types.NewIdentifiedChannel(testPort1, testChannel1, - suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, - types.OPEN, types.ORDERED, testConnectionIDA, - ), + types.NewIdentifiedChannel( + channelA0.PortID, + channelA0.ID, + suite.chainA.GetChannel(channelA0), ), ) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, - testClientIDB, testClientIDA, - connection.OPEN, - ) + // create second connection + connA1, connB1 := suite.coordinator.CreateConnection(suite.chainA, suite.chainB, clientA, clientB) + + // create second channel on second connection + channelA1, _ := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA1, connB1, types.ORDERED) + channels = append(channels, - types.NewIdentifiedChannel(testPort2, testChannel2, - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, - types.OPEN, types.ORDERED, testConnectionIDB, - ), + types.NewIdentifiedChannel( + channelA1.PortID, + channelA1.ID, + suite.chainA.GetChannel(channelA1), ), ) // set expected result - expRes, err = codec.MarshalJSONIndent(suite.cdc, channels) + expRes, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), channels) suite.Require().NoError(err) }, }, { "success with singular connection channels", func() { - suite.SetupTest() channels := make([]types.IdentifiedChannel, 0, 2) - // create channels on singular connections - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, - testClientIDA, testClientIDB, - connection.OPEN, - ) + // create first connection/channel + _, _, connA, connB, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) channels = append(channels, - types.NewIdentifiedChannel(testPort1, testChannel1, - suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, - types.OPEN, types.ORDERED, testConnectionIDA, - ), + types.NewIdentifiedChannel( + channelA0.PortID, + channelA0.ID, + suite.chainA.GetChannel(channelA0), ), ) + + // create second channel on the same connection + channelA1, _ := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.UNORDERED) channels = append(channels, - types.NewIdentifiedChannel(testPort2, testChannel2, - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, - types.OPEN, types.UNORDERED, testConnectionIDA, - ), + types.NewIdentifiedChannel( + channelA1.PortID, + channelA1.ID, + suite.chainA.GetChannel(channelA1), ), ) // set expected result - expRes, err = codec.MarshalJSONIndent(suite.cdc, channels) + expRes, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), channels) suite.Require().NoError(err) }, }, { "success no channels", func() { - suite.SetupTest() - expRes, err = codec.MarshalJSONIndent(suite.cdc, []types.IdentifiedChannel{}) + expRes, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), []types.IdentifiedChannel{}) suite.Require().NoError(err) }, }, } for i, tc := range testCases { + suite.SetupTest() // reset tc.setup() - bz, err := suite.querier(suite.chainA.GetContext(), path, query) + bz, err := suite.chainA.Querier(suite.chainA.GetContext(), path, query) suite.Require().NoError(err, "test case %d failed: %s", i, tc.name) suite.Require().Equal(expRes, bz, "test case %d failed: %s", i, tc.name) @@ -127,18 +123,10 @@ func (suite *KeeperTestSuite) TestQueryConnectionChannels() { var ( expRes []byte + params types.QueryConnectionChannelsParams err error ) - params := types.NewQueryConnectionChannelsParams(testConnectionIDA, 1, 100) - data, err := suite.cdc.MarshalJSON(params) - suite.Require().NoError(err) - - query := abci.RequestQuery{ - Path: "", - Data: data, - } - testCases := []struct { name string setup func() @@ -146,101 +134,106 @@ func (suite *KeeperTestSuite) TestQueryConnectionChannels() { { "success with singular connection channels", func() { - suite.SetupTest() channels := make([]types.IdentifiedChannel, 0, 2) - // create channels on singular connections - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, - testClientIDA, testClientIDB, - connection.OPEN, - ) + // create first connection/channel + _, _, connA, connB, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) channels = append(channels, - types.NewIdentifiedChannel(testPort1, testChannel1, - suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, - types.OPEN, types.ORDERED, testConnectionIDA, - ), + types.NewIdentifiedChannel( + channelA0.PortID, + channelA0.ID, + suite.chainA.GetChannel(channelA0), ), ) + + // create second channel on the same connection + channelA1, _ := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) channels = append(channels, - types.NewIdentifiedChannel(testPort2, testChannel2, - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, - types.OPEN, types.UNORDERED, testConnectionIDA, - ), + types.NewIdentifiedChannel( + channelA1.PortID, + channelA1.ID, + suite.chainA.GetChannel(channelA1), ), ) + params = types.NewQueryConnectionChannelsParams(connA.ID, 1, 100) + // set expected result - expRes, err = codec.MarshalJSONIndent(suite.cdc, channels) + expRes, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), channels) suite.Require().NoError(err) }, }, { "success multiple connection channels", func() { - suite.SetupTest() channels := make([]types.IdentifiedChannel, 0, 1) - // create channels on different connections - suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, - testClientIDA, testClientIDB, - connection.OPEN, - ) + // create first connection/channel + clientA, clientB, connA, _, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + channels = append(channels, - types.NewIdentifiedChannel(testPort1, testChannel1, - suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, - types.OPEN, types.ORDERED, testConnectionIDA, - ), + types.NewIdentifiedChannel( + channelA0.PortID, + channelA0.ID, + suite.chainA.GetChannel(channelA0), ), ) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, - testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort2, testChannel2, testPort1, testChannel1, - types.OPEN, types.ORDERED, testConnectionIDB, - ) + // create second connection + connA1, connB1 := suite.coordinator.CreateConnection(suite.chainA, suite.chainB, clientA, clientB) + + // create second channel on second connection + suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA1, connB1, types.ORDERED) + + params = types.NewQueryConnectionChannelsParams(connA.ID, 1, 100) // set expected result - expRes, err = codec.MarshalJSONIndent(suite.cdc, channels) + expRes, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), channels) suite.Require().NoError(err) }, }, { "success no channels", func() { - suite.SetupTest() - expRes, err = codec.MarshalJSONIndent(suite.cdc, []types.IdentifiedChannel{}) + // create connection but no channels + _, _, connA, _ := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + params = types.NewQueryConnectionChannelsParams(connA.ID, 1, 100) + + expRes, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), []types.IdentifiedChannel{}) suite.Require().NoError(err) }, }, } for i, tc := range testCases { + suite.SetupTest() // reset tc.setup() - bz, err := suite.querier(suite.chainA.GetContext(), path, query) + data, err := suite.chainA.App.AppCodec().MarshalJSON(params) + suite.Require().NoError(err) + + query := abci.RequestQuery{ + Path: "", + Data: data, + } + + bz, err := suite.chainA.Querier(suite.chainA.GetContext(), path, query) suite.Require().NoError(err, "test case %d failed: %s", i, tc.name) suite.Require().Equal(expRes, bz, "test case %d failed: %s", i, tc.name) } } +// TestQuerierChannelClientState verifies correct querying of client state associated +// with a channel end. func (suite *KeeperTestSuite) TestQuerierChannelClientState() { path := []string{types.SubModuleName, types.QueryChannelClientState} - params := types.NewQueryChannelClientStateParams(testPort1, testChannel1) - data, err := suite.cdc.MarshalJSON(params) - suite.Require().NoError(err) - query := abci.RequestQuery{ - Path: "", - Data: data, - } + var ( + clientID string + params types.QueryChannelClientStateParams + ) testCases := []struct { name string @@ -249,77 +242,81 @@ func (suite *KeeperTestSuite) TestQuerierChannelClientState() { }{ { "channel not found", - func() {}, + func() { + clientA, err := suite.coordinator.CreateClient(suite.chainA, suite.chainB, clientexported.Tendermint) + suite.Require().NoError(err) + + clientID = clientA + params = types.NewQueryChannelClientStateParams("doesnotexist", "doesnotexist") + }, false, }, { "connection for channel not found", func() { - _ = suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, - types.OPEN, types.ORDERED, testConnectionIDA, - ) + // connection for channel is deleted from state + clientA, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + + channel := suite.chainA.GetChannel(channelA) + channel.ConnectionHops[0] = "doesnotexist" + + // set connection hops to wrong connection ID + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel) + + clientID = clientA + params = types.NewQueryChannelClientStateParams(channelA.PortID, channelA.ID) + }, false, }, { "client state for channel's connection not found", func() { - _ = suite.chainA.createConnection( - testConnectionIDA, testConnectionIDB, - testClientIDA, testClientIDB, - connection.OPEN, - ) - _ = suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, - types.OPEN, types.ORDERED, testConnectionIDA, - ) + clientA, _, connA, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + + // setting connection to empty results in wrong clientID used + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, connectiontypes.ConnectionEnd{}) + + clientID = clientA + params = types.NewQueryChannelClientStateParams(channelA.PortID, channelA.ID) }, false, }, { "success", func() { - err = suite.chainA.CreateClient(suite.chainB) - suite.Require().NoError(err) - err = suite.chainB.CreateClient(suite.chainA) - suite.Require().NoError(err) - suite.chainA.createConnection( - testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, - connection.OPEN, - ) - suite.chainB.createConnection( - testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, - connection.OPEN, - ) - suite.chainA.createChannel( - testPort1, testChannel1, testPort2, testChannel2, types.INIT, - types.ORDERED, testConnectionIDB, - ) - suite.chainB.createChannel( - testPort2, testChannel2, testPort1, testChannel1, types.TRYOPEN, - types.ORDERED, testConnectionIDA, - ) + clientA, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + + clientID = clientA + params = types.NewQueryChannelClientStateParams(channelA.PortID, channelA.ID) }, true, }, } for i, tc := range testCases { + suite.SetupTest() // reset tc.setup() - clientState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), testClientIDB) - bz, err := suite.querier(suite.chainA.GetContext(), path, query) + data, err := suite.chainA.App.AppCodec().MarshalJSON(params) + suite.Require().NoError(err) + + query := abci.RequestQuery{ + Path: "", + Data: data, + } + + clientState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientID) + bz, err := suite.chainA.Querier(suite.chainA.GetContext(), path, query) if tc.expPass { // set expected result - expRes, merr := codec.MarshalJSONIndent(suite.cdc, clientState) + expRes, merr := codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), clientState) suite.Require().NoError(merr) - suite.Require().True(found) + suite.Require().True(found, "test case %d failed: %s", i, tc.name) suite.Require().NoError(err, "test case %d failed: %s", i, tc.name) suite.Require().Equal(string(expRes), string(bz), "test case %d failed: %s", i, tc.name) } else { - suite.Require().False(found) suite.Require().Error(err, "test case %d passed: %s", i, tc.name) suite.Require().Nil(bz, "test case %d passed: %s", i, tc.name) } @@ -332,17 +329,10 @@ func (suite *KeeperTestSuite) TestQueryPacketCommitments() { var ( expRes []byte + params types.QueryPacketCommitmentsParams + err error ) - params := types.NewQueryPacketCommitmentsParams(testPort1, testChannel1, 1, 100) - data, err := suite.cdc.MarshalJSON(params) - suite.Require().NoError(err) - - query := abci.RequestQuery{ - Path: "", - Data: data, - } - testCases := []struct { name string setup func() @@ -350,58 +340,77 @@ func (suite *KeeperTestSuite) TestQueryPacketCommitments() { { "success", func() { - suite.SetupTest() - ctx := suite.chainA.GetContext() + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + seq := uint64(1) commitments := []uint64{} // create several commitments on the same channel and port for i := seq; i < 10; i++ { - suite.chainA.storePacketCommitment(ctx, testPort1, testChannel1, i) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), channelA.PortID, channelA.ID, i, []byte("ack")) commitments = append(commitments, i) } - expRes, err = codec.MarshalJSONIndent(suite.cdc, commitments) + params = types.NewQueryPacketCommitmentsParams(channelA.PortID, channelA.ID, 1, 100) + + expRes, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), commitments) suite.Require().NoError(err) }, }, { "success with multiple channels", func() { - suite.SetupTest() - ctx := suite.chainA.GetContext() + _, _, connA, connB, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + seq := uint64(1) commitments := []uint64{} // create several commitments on the same channel and port for i := seq; i < 10; i++ { - suite.chainA.storePacketCommitment(ctx, testPort1, testChannel1, i) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), channelA0.PortID, channelA0.ID, i, []byte("ack")) commitments = append(commitments, i) } + // create second channel + channelA1, _ := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + // create several commitments on a different channel and port for i := seq; i < 10; i++ { - suite.chainA.storePacketCommitment(ctx, testPort2, testChannel2, i) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), channelA1.PortID, channelA1.ID, i, []byte("ack")) } - expRes, err = codec.MarshalJSONIndent(suite.cdc, commitments) + params = types.NewQueryPacketCommitmentsParams(channelA0.PortID, channelA1.ID, 1, 100) + + expRes, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), commitments) suite.Require().NoError(err) }, }, { "success no packet commitments", func() { - suite.SetupTest() - expRes, err = codec.MarshalJSONIndent(suite.cdc, []uint64{}) + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + + params = types.NewQueryPacketCommitmentsParams(channelA.PortID, channelA.ID, 1, 100) + + expRes, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), []uint64{}) suite.Require().NoError(err) }, }, } for i, tc := range testCases { + suite.SetupTest() // reset tc.setup() - bz, err := suite.querier(suite.chainA.GetContext(), path, query) + data, err := suite.chainA.App.AppCodec().MarshalJSON(params) + suite.Require().NoError(err) + + query := abci.RequestQuery{ + Path: "", + Data: data, + } + + bz, err := suite.chainA.Querier(suite.chainA.GetContext(), path, query) suite.Require().NoError(err, "test case %d failed: %s", i, tc.name) suite.Require().Equal(expRes, bz, "test case %d failed: %s", i, tc.name) @@ -419,17 +428,10 @@ func (suite *KeeperTestSuite) TestQueryUnrelayedAcks() { var ( expResAck []byte expResSend []byte + params types.QueryUnrelayedPacketsParams + err error ) - params := types.NewQueryUnrelayedPacketsParams(testPort1, testChannel1, sequences, 1, 100) - data, err := suite.cdc.MarshalJSON(params) - suite.Require().NoError(err) - - query := abci.RequestQuery{ - Path: "", - Data: data, - } - testCases := []struct { name string setup func() @@ -437,25 +439,27 @@ func (suite *KeeperTestSuite) TestQueryUnrelayedAcks() { { "success", func() { - suite.SetupTest() - ctx := suite.chainA.GetContext() + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + unrelayedAcks := []uint64{} unrelayedSends := []uint64{} // create acknowledgements for first 3 sequences for _, seq := range sequences { if seq < 4 { - suite.chainA.storeAcknowledgement(ctx, testPort1, testChannel1, seq) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), channelA.PortID, channelA.ID, seq, []byte("ack")) unrelayedAcks = append(unrelayedAcks, seq) } else { unrelayedSends = append(unrelayedSends, seq) } } - expResAck, err = codec.MarshalJSONIndent(suite.cdc, unrelayedAcks) + params = types.NewQueryUnrelayedPacketsParams(channelA.PortID, channelA.ID, sequences, 1, 100) + + expResAck, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), unrelayedAcks) suite.Require().NoError(err) - expResSend, err = codec.MarshalJSONIndent(suite.cdc, unrelayedSends) + expResSend, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), unrelayedSends) suite.Require().NoError(err) }, @@ -463,64 +467,80 @@ func (suite *KeeperTestSuite) TestQueryUnrelayedAcks() { { "success with multiple channels", func() { - suite.SetupTest() - ctx := suite.chainA.GetContext() + _, _, connA, connB, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + ctxA := suite.chainA.GetContext() + unrelayedAcks := []uint64{} unrelayedSends := []uint64{} // create acknowledgements for first 3 sequences for _, seq := range sequences { if seq < 4 { - suite.chainA.storeAcknowledgement(ctx, testPort1, testChannel1, seq) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctxA, channelA0.PortID, channelA0.ID, seq, []byte("ack")) unrelayedAcks = append(unrelayedAcks, seq) } else { unrelayedSends = append(unrelayedSends, seq) } } + // create second channel + channelA1, _ := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.UNORDERED) + // create acknowledgements for other sequences on different channel/port for _, seq := range sequences { if seq >= 4 { - suite.chainA.storeAcknowledgement(ctx, testPort2, testChannel2, seq) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctxA, channelA1.PortID, channelA1.ID, seq, []byte("ack")) } } - expResAck, err = codec.MarshalJSONIndent(suite.cdc, unrelayedAcks) + params = types.NewQueryUnrelayedPacketsParams(channelA0.PortID, channelA0.ID, sequences, 1, 100) + + expResAck, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), unrelayedAcks) suite.Require().NoError(err) - expResSend, err = codec.MarshalJSONIndent(suite.cdc, unrelayedSends) + expResSend, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), unrelayedSends) suite.Require().NoError(err) }, }, { "success no unrelayed acks", func() { - suite.SetupTest() - ctx := suite.chainA.GetContext() + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) // create acknowledgements for all sequences for _, seq := range sequences { - suite.chainA.storeAcknowledgement(ctx, testPort1, testChannel1, seq) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), channelA.PortID, channelA.ID, seq, []byte("ack")) } - expResSend, err = codec.MarshalJSONIndent(suite.cdc, []uint64{}) + params = types.NewQueryUnrelayedPacketsParams(channelA.PortID, channelA.ID, sequences, 1, 100) + + expResSend, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), []uint64{}) suite.Require().NoError(err) - expResAck, err = codec.MarshalJSONIndent(suite.cdc, sequences) + expResAck, err = codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), sequences) suite.Require().NoError(err) }, }, } for i, tc := range testCases { + suite.SetupTest() // reset tc.setup() - bz, err := suite.querier(suite.chainA.GetContext(), pathAck, query) + data, err := suite.chainA.App.AppCodec().MarshalJSON(params) + suite.Require().NoError(err) + + query := abci.RequestQuery{ + Path: "", + Data: data, + } + + bz, err := suite.chainA.Querier(suite.chainA.GetContext(), pathAck, query) suite.Require().NoError(err, "test case %d failed: %s", i, tc.name) suite.Require().Equal(expResAck, bz, "test case %d failed: %s", i, tc.name) - bz, err = suite.querier(suite.chainA.GetContext(), pathSend, query) + bz, err = suite.chainA.Querier(suite.chainA.GetContext(), pathSend, query) suite.Require().NoError(err, "test case %d failed: %s", i, tc.name) suite.Require().Equal(expResSend, bz, "test case %d failed: %s", i, tc.name) diff --git a/x/ibc/04-channel/keeper/timeout_test.go b/x/ibc/04-channel/keeper/timeout_test.go index 44d0f2cf6..29ee607cb 100644 --- a/x/ibc/04-channel/keeper/timeout_test.go +++ b/x/ibc/04-channel/keeper/timeout_test.go @@ -4,111 +4,154 @@ import ( "fmt" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) +// TestTimeoutPacket test the TimeoutPacket call on chainA by ensuring the timeout has passed +// on chainB, but that no ack has been written yet. Test cases expected to reach proof +// verification must specify which proof to use using the ordered bool. func (suite *KeeperTestSuite) TestTimeoutPacket() { - counterparty := types.NewCounterparty(testPort2, testChannel2) - packetKey := host.KeyPacketAcknowledgement(testPort2, testChannel2, 2) var ( packet types.Packet nextSeqRecv uint64 + ordered bool ) testCases := []testCase{ - {"success", func() { - nextSeqRecv = 1 - packet = types.NewPacket(newMockTimeoutPacket().GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), 1, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDA, testConnectionIDB, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.OPEN, types.UNORDERED, testConnectionIDB) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 2, types.CommitPacket(packet)) + {"success: ORDERED", func() { + ordered = true + + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) }, true}, - {"channel not found", func() {}, false}, + {"success: UNORDERED", func() { + ordered = false + + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) + }, true}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, {"channel not open", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.CLOSED, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) }, false}, - {"packet source port ≠ channel counterparty port", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort3, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + {"packet destination port ≠ channel counterparty port", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, - {"packet source channel ID ≠ channel counterparty channel ID", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel3, types.OPEN, types.ORDERED, testConnectionIDA) + {"packet destination channel ID ≠ channel counterparty channel ID", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong channel for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"connection not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, ibctesting.ChannelVersion), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) }, false}, {"timeout", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 10, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) }, false}, {"packet already received ", func() { + ordered = true nextSeqRecv = 2 - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) }, false}, {"packet hasn't been sent", func() { - nextSeqRecv = 1 - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + clientA, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) }, false}, {"next seq receive verification failed", func() { - nextSeqRecv = 1 - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 2, types.CommitPacket(packet)) + // set ordered to false resulting in wrong proof provided + ordered = false + + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) }, false}, {"packet ack verification failed", func() { - nextSeqRecv = 1 - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 2, types.CommitPacket(packet)) + // set ordered to true resulting in wrong proof provided + ordered = true + + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) }, false}, } for i, tc := range testCases { tc := tc suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { + var ( + proof []byte + proofHeight uint64 + ) + suite.SetupTest() // reset + nextSeqRecv = 1 // must be explicitly changed tc.malleate() - ctx := suite.chainB.GetContext() + orderedPacketKey := host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel()) + unorderedPacketKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - suite.chainB.updateClient(suite.chainA) - suite.chainA.updateClient(suite.chainB) - proof, proofHeight := queryProof(suite.chainA, packetKey) + if ordered { + proof, proofHeight = suite.chainB.QueryProof(orderedPacketKey) + } else { + proof, proofHeight = suite.chainB.QueryProof(unorderedPacketKey) + } + + _, err := suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutPacket(suite.chainA.GetContext(), packet, proof, proofHeight, nextSeqRecv) if tc.expPass { - packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.TimeoutPacket(ctx, packet, proof, proofHeight+1, nextSeqRecv) suite.Require().NoError(err) - suite.Require().NotNil(packetOut) } else { - packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.TimeoutPacket(ctx, packet, proof, proofHeight, nextSeqRecv) suite.Require().Error(err) - suite.Require().Nil(packetOut) } }) } } -// TestTimeoutExectued verifies that packet commitments are deleted after -// capabilities are verified. +// TestTimeoutExectued verifies that packet commitments are deleted on chainA after the +// channel capabilities are verified. func (suite *KeeperTestSuite) TestTimeoutExecuted() { - sequence := uint64(1) - var ( packet types.Packet chanCap *capabilitytypes.Capability @@ -116,14 +159,24 @@ func (suite *KeeperTestSuite) TestTimeoutExecuted() { testCases := []testCase{ {"success ORDERED", func() { - packet = types.NewPacket(newMockTimeoutPacket().GetBytes(), 1, testPort1, testChannel1, testPort2, testChannel2, timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), sequence, types.CommitPacket(packet)) + _, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, true}, - {"channel not found", func() {}, false}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, {"incorrect capability", func() { - packet = types.NewPacket(newMockTimeoutPacket().GetBytes(), 1, testPort1, testChannel1, testPort2, testChannel2, timeoutHeight, disabledTimeoutTimestamp) - suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + chanCap = capabilitytypes.NewCapability(100) }, false}, } @@ -133,16 +186,10 @@ func (suite *KeeperTestSuite) TestTimeoutExecuted() { suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { suite.SetupTest() // reset - var err error - chanCap, err = suite.chainA.App.ScopedIBCKeeper.NewCapability( - suite.chainA.GetContext(), host.ChannelCapabilityPath(testPort1, testChannel1), - ) - suite.Require().NoError(err, "could not create capability") - tc.malleate() - err = suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutExecuted(suite.chainA.GetContext(), chanCap, packet) - pc := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), sequence) + err := suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutExecuted(suite.chainA.GetContext(), chanCap, packet) + pc := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if tc.expPass { suite.NoError(err) @@ -154,87 +201,131 @@ func (suite *KeeperTestSuite) TestTimeoutExecuted() { } } +// TestTimeoutOnClose tests the call TimeoutOnClose on chainA by closing the corresponding +// channel on chainB after the packet commitment has been created. func (suite *KeeperTestSuite) TestTimeoutOnClose() { - channelKey := host.KeyChannel(testPort2, testChannel2) - unorderedPacketKey := host.KeyPacketAcknowledgement(testPort2, testChannel2, 2) - orderedPacketKey := host.KeyNextSequenceRecv(testPort2, testChannel2) - - counterparty := types.NewCounterparty(testPort2, testChannel2) var ( packet types.Packet + chanCap *capabilitytypes.Capability nextSeqRecv uint64 ordered bool ) testCases := []testCase{ - {"success on ordered channel", func() { + {"success: ORDERED", func() { ordered = true - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.CLOSED, types.ORDERED, testConnectionIDB) // channel on chainA is closed - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 2, types.CommitPacket(packet)) - suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainA.GetContext(), testPort2, testChannel2, nextSeqRecv) + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) + + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, true}, - {"success on unordered channel", func() { + {"success: UNORDERED", func() { ordered = false - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainA.CreateClient(suite.chainB) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainA.createConnection(testConnectionIDB, testConnectionIDA, testClientIDB, testClientIDA, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainA.createChannel(testPort2, testChannel2, testPort1, testChannel1, types.CLOSED, types.UNORDERED, testConnectionIDB) // channel on chainA is closed - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 2, types.CommitPacket(packet)) + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) + + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, true}, - {"channel not found", func() {}, false}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, {"packet dest port ≠ channel counterparty port", func() { - ordered = true - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort3, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"packet dest channel ID ≠ channel counterparty channel ID", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel3, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + // use wrong channel for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"connection not found", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 1, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, ibctesting.ChannelVersion), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create chancap + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"packet hasn't been sent", func() { - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet already received", func() { + nextSeqRecv = 2 + ordered = true + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) + + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"channel verification failed", func() { - ordered = false - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 2, types.CommitPacket(packet)) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainB.GetContext(), testPort1, testChannel1, nextSeqRecv) + ordered = true + _, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"next seq receive verification failed", func() { - ordered = true - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.ORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 2, types.CommitPacket(packet)) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainB.GetContext(), testPort1, testChannel1, nextSeqRecv) + // set ordered to false providing the wrong proof for ORDERED case + ordered = false + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) }, false}, {"packet ack verification failed", func() { - ordered = false - packet = types.NewPacket(mockSuccessPacket{}.GetBytes(), 2, testPort1, testChannel1, counterparty.GetPortID(), counterparty.GetChannelID(), timeoutHeight, disabledTimeoutTimestamp) - suite.chainB.CreateClient(suite.chainA) - suite.chainB.createConnection(testConnectionIDA, testConnectionIDB, testClientIDA, testClientIDB, connection.OPEN) - suite.chainB.createChannel(testPort1, testChannel1, testPort2, testChannel2, types.OPEN, types.UNORDERED, testConnectionIDA) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainB.GetContext(), testPort1, testChannel1, 2, types.CommitPacket(packet)) - suite.chainB.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainB.GetContext(), testPort1, testChannel1, nextSeqRecv) + // set ordered to true providing the wrong proof for UNORDERED case + ordered = true + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel capability not found", func() { + ordered = true + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint) + channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint) + + chanCap = capabilitytypes.NewCapability(100) }, false}, } @@ -244,42 +335,29 @@ func (suite *KeeperTestSuite) TestTimeoutOnClose() { var proof []byte suite.SetupTest() // reset + nextSeqRecv = 1 // must be explicitly changed tc.malleate() - suite.chainB.updateClient(suite.chainA) - suite.chainA.updateClient(suite.chainB) - proofClosed, proofHeight := queryProof(suite.chainA, channelKey) + channelKey := host.KeyChannel(packet.GetDestPort(), packet.GetDestChannel()) + unorderedPacketKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + orderedPacketKey := host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel()) + + proofClosed, proofHeight := suite.chainB.QueryProof(channelKey) if ordered { - proof, _ = queryProof(suite.chainA, orderedPacketKey) + proof, _ = suite.chainB.QueryProof(orderedPacketKey) } else { - proof, _ = queryProof(suite.chainA, unorderedPacketKey) + proof, _ = suite.chainB.QueryProof(unorderedPacketKey) } - ctx := suite.chainB.GetContext() - cap, err := suite.chainB.App.ScopedIBCKeeper.NewCapability(ctx, host.ChannelCapabilityPath(testPort1, testChannel1)) - suite.Require().NoError(err) + _, err := suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutOnClose(suite.chainA.GetContext(), chanCap, packet, proof, proofClosed, proofHeight, nextSeqRecv) if tc.expPass { - packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.TimeoutOnClose(ctx, cap, packet, proof, proofClosed, proofHeight+1, nextSeqRecv) suite.Require().NoError(err) - suite.Require().NotNil(packetOut) } else { - // switch the proofs to invalidate them - packetOut, err := suite.chainB.App.IBCKeeper.ChannelKeeper.TimeoutOnClose(ctx, cap, packet, proofClosed, proof, proofHeight+1, nextSeqRecv) suite.Require().Error(err) - suite.Require().Nil(packetOut) } }) } } - -type mockTimeoutPacket struct{} - -func newMockTimeoutPacket() mockTimeoutPacket { - return mockTimeoutPacket{} -} - -// GetBytes returns the serialised packet data (without timeout) -func (mp mockTimeoutPacket) GetBytes() []byte { return []byte("THIS IS A TIMEOUT PACKET") } diff --git a/x/ibc/04-channel/types/msgs.go b/x/ibc/04-channel/types/msgs.go index e52b83b8e..243b95fb8 100644 --- a/x/ibc/04-channel/types/msgs.go +++ b/x/ibc/04-channel/types/msgs.go @@ -12,7 +12,7 @@ import ( var _ sdk.Msg = &MsgChannelOpenInit{} -// NewMsgChannelOpenInit creates a new MsgChannelCloseInit MsgChannelOpenInit +// NewMsgChannelOpenInit creates a new MsgChannelOpenInit func NewMsgChannelOpenInit( portID, channelID string, version string, channelOrder Order, connectionHops []string, counterpartyPortID, counterpartyChannelID string, signer sdk.AccAddress, diff --git a/x/ibc/testing/chain.go b/x/ibc/testing/chain.go index add5c17db..99acf6bbd 100644 --- a/x/ibc/testing/chain.go +++ b/x/ibc/testing/chain.go @@ -2,6 +2,7 @@ package testing import ( "fmt" + "strconv" "testing" "time" @@ -9,14 +10,20 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/tendermint/tendermint/crypto/tmhash" tmmath "github.com/tendermint/tendermint/libs/math" lite "github.com/tendermint/tendermint/lite2" tmtypes "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/version" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" @@ -30,17 +37,18 @@ const ( UnbondingPeriod time.Duration = time.Hour * 24 * 7 * 3 MaxClockDrift time.Duration = time.Second * 10 - ConnectionVersion = "1.0" - ChannelVersion = "1.0" + ConnectionVersion = "1.0.0" + ChannelVersion = "ics20-1" + InvalidID = "IDisInvalid" - ClientIDPrefix = "clientFor" ConnectionIDPrefix = "connectionid" - ChannelIDPrefix = "channelid" - PortIDPrefix = "portid" + + maxInt = int(^uint(0) >> 1) ) var ( DefaultTrustLevel tmmath.Fraction = lite.DefaultTrustLevel + TestHash = []byte("TESTING HASH") ) // TestChain is a testing struct that wraps a simapp with the last TM Header, the current ABCI @@ -64,9 +72,8 @@ type TestChain struct { SenderAccount authtypes.AccountI // IBC specific helpers - ClientIDs []string // ClientID's used on this chain - Connections []TestConnection // track connectionID's created for this chain - Channels []TestChannel // track portID/channelID's created for this chain + ClientIDs []string // ClientID's used on this chain + Connections []*TestConnection // track connectionID's created for this chain } // NewTestChain initializes a new TestChain instance with a single validator set using a @@ -88,43 +95,40 @@ func NewTestChain(t *testing.T, chainID string) *TestChain { valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) signers := []tmtypes.PrivValidator{privVal} - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, - abci.Header{ - Height: 1, - Time: globalStartTime, - }, - ) - - // generate and set SenderAccount + // generate genesis account senderPrivKey := secp256k1.GenPrivKey() - simapp.AddTestAddrsFromPubKeys(app, ctx, []crypto.PubKey{senderPrivKey.PubKey()}, sdk.NewInt(10000000000)) - acc := app.AccountKeeper.GetAccount(ctx, sdk.AccAddress(senderPrivKey.PubKey().Address())) + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + } + + app := simapp.SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance) - // commit init chain changes so create client can be called by a counterparty chain - app.Commit() // create current header and call begin block header := abci.Header{ - Height: 2, - Time: globalStartTime.Add(timeIncrement), + Height: 1, + Time: globalStartTime, } - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - - lastHeader := ibctmtypes.CreateTestHeader(chainID, 1, globalStartTime, valSet, signers) // create an account to send transactions from - return &TestChain{ + chain := &TestChain{ t: t, ChainID: chainID, App: app, - LastHeader: lastHeader, CurrentHeader: header, Querier: keeper.NewQuerier(*app.IBCKeeper), Vals: valSet, Signers: signers, senderPrivKey: senderPrivKey, SenderAccount: acc, + ClientIDs: make([]string, 0), + Connections: make([]*TestConnection, 0), } + + chain.NextBlock() + + return chain } // GetContext returns the current context for the application. @@ -133,11 +137,11 @@ func (chain *TestChain) GetContext() sdk.Context { } // QueryProof performs an abci query with the given key and returns the proto encoded merkle proof -// for the query and the height at which the query was performed. +// for the query and the height at which the proof will succeed on a tendermint verifier. func (chain *TestChain) QueryProof(key []byte) ([]byte, uint64) { res := chain.App.Query(abci.RequestQuery{ Path: fmt.Sprintf("store/%s/key", host.StoreKey), - Height: chain.App.LastBlockHeight(), + Height: chain.App.LastBlockHeight() - 1, Data: key, Prove: true, }) @@ -149,7 +153,10 @@ func (chain *TestChain) QueryProof(key []byte) ([]byte, uint64) { proof, err := chain.App.AppCodec().MarshalBinaryBare(&merkleProof) require.NoError(chain.t, err) - return proof, uint64(res.Height) + // proof height + 1 is returned as the proof created corresponds to the height the proof + // was created in the IAVL tree. Tendermint and subsequently the clients that rely on it + // have heights 1 above the IAVL tree. Thus we return proof height + 1 + return proof, uint64(res.Height) + 1 } // NextBlock sets the last header to the current header and increments the current header to be @@ -158,18 +165,19 @@ func (chain *TestChain) QueryProof(key []byte) ([]byte, uint64) { // CONTRACT: this function must only be called after app.Commit() occurs func (chain *TestChain) NextBlock() { // set the last header to the current header - chain.LastHeader = ibctmtypes.CreateTestHeader( - chain.CurrentHeader.ChainID, - chain.CurrentHeader.Height, - chain.CurrentHeader.Time, - chain.Vals, chain.Signers, - ) + chain.LastHeader = chain.CreateTMClientHeader() // increment the current header chain.CurrentHeader = abci.Header{ - Height: chain.CurrentHeader.Height + 1, - Time: chain.CurrentHeader.Time, + Height: chain.App.LastBlockHeight() + 1, + AppHash: chain.App.LastCommitID().Hash, + // NOTE: the time is increased by the coordinator to maintain time synchrony amongst + // chains. + Time: chain.CurrentHeader.Time, } + + chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) + } // SendMsg delivers a transaction through the application. It updates the senders sequence @@ -198,11 +206,51 @@ func (chain *TestChain) SendMsg(msg sdk.Msg) error { return nil } +// GetClientState retreives the client state for the provided clientID. The client is +// expected to exist otherwise testing will fail. +func (chain *TestChain) GetClientState(clientID string) clientexported.ClientState { + clientState, found := chain.App.IBCKeeper.ClientKeeper.GetClientState(chain.GetContext(), clientID) + require.True(chain.t, found) + + return clientState +} + +// GetConnection retreives an IBC Connection for the provided TestConnection. The +// connection is expected to exist otherwise testing will fail. +func (chain *TestChain) GetConnection(testConnection *TestConnection) connectiontypes.ConnectionEnd { + connection, found := chain.App.IBCKeeper.ConnectionKeeper.GetConnection(chain.GetContext(), testConnection.ID) + require.True(chain.t, found) + + return connection +} + +// GetChannel retreives an IBC Channel for the provided TestChannel. The channel +// is expected to exist otherwise testing will fail. +func (chain *TestChain) GetChannel(testChannel TestChannel) channeltypes.Channel { + channel, found := chain.App.IBCKeeper.ChannelKeeper.GetChannel(chain.GetContext(), testChannel.PortID, testChannel.ID) + require.True(chain.t, found) + + return channel +} + +// GetAcknowledgement retreives an acknowledgement for the provided packet. If the +// acknowledgement does not exist then testing will fail. +func (chain *TestChain) GetAcknowledgement(packet channelexported.PacketI) []byte { + ack, found := chain.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(chain.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + require.True(chain.t, found) + + return ack +} + +// GetPrefix returns the prefix for used by a chain in connection creation +func (chain *TestChain) GetPrefix() commitmenttypes.MerklePrefix { + return commitmenttypes.NewMerklePrefix(chain.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix().Bytes()) +} + // NewClientID appends a new clientID string in the format: // ClientFor func (chain *TestChain) NewClientID(counterpartyChainID string) string { - clientID := ClientIDPrefix + counterpartyChainID + string(len(chain.ClientIDs)) - + clientID := "client" + strconv.Itoa(len(chain.ClientIDs)) + "For" + counterpartyChainID chain.ClientIDs = append(chain.ClientIDs, clientID) return clientID } @@ -210,9 +258,9 @@ func (chain *TestChain) NewClientID(counterpartyChainID string) string { // NewConnection appends a new TestConnection which contains references to the connection id, // client id and counterparty client id. The connection id format: // connectionid -func (chain *TestChain) NewTestConnection(clientID, counterpartyClientID string) TestConnection { - connectionID := ConnectionIDPrefix + string(len(chain.Connections)) - conn := TestConnection{ +func (chain *TestChain) NewTestConnection(clientID, counterpartyClientID string) *TestConnection { + connectionID := ConnectionIDPrefix + strconv.Itoa(len(chain.Connections)) + conn := &TestConnection{ ID: connectionID, ClientID: clientID, CounterpartyClientID: counterpartyClientID, @@ -222,23 +270,6 @@ func (chain *TestChain) NewTestConnection(clientID, counterpartyClientID string) return conn } -// NewTestChannel appends a new TestChannel which contains references to the port and channel ID -// used for channel creation and interaction. The channel id and port id format: -// channelid -// portid -func (chain *TestChain) NewTestChannel() TestChannel { - portID := PortIDPrefix + string(len(chain.Channels)) - channelID := ChannelIDPrefix + string(len(chain.Channels)) - channel := TestChannel{ - PortID: portID, - ChannelID: channelID, - } - - chain.Channels = append(chain.Channels, channel) - - return channel -} - // CreateTMClient will construct and execute a 07-tendermint MsgCreateClient. A counterparty // client will be created on the (target) chain. func (chain *TestChain) CreateTMClient(counterparty *TestChain, clientID string) error { @@ -263,17 +294,65 @@ func (chain *TestChain) UpdateTMClient(counterparty *TestChain, clientID string) return chain.SendMsg(msg) } +// CreateTMClientHeader creates a TM header to update the TM client. +func (chain *TestChain) CreateTMClientHeader() ibctmtypes.Header { + vsetHash := chain.Vals.Hash() + tmHeader := tmtypes.Header{ + Version: version.Consensus{Block: 2, App: 2}, + ChainID: chain.ChainID, + Height: chain.CurrentHeader.Height, + Time: chain.CurrentHeader.Time, + LastBlockID: MakeBlockID(make([]byte, tmhash.Size), maxInt, make([]byte, tmhash.Size)), + LastCommitHash: chain.App.LastCommitID().Hash, + DataHash: tmhash.Sum([]byte("data_hash")), + ValidatorsHash: vsetHash, + NextValidatorsHash: vsetHash, + ConsensusHash: tmhash.Sum([]byte("consensus_hash")), + AppHash: chain.CurrentHeader.AppHash, + LastResultsHash: tmhash.Sum([]byte("last_results_hash")), + EvidenceHash: tmhash.Sum([]byte("evidence_hash")), + ProposerAddress: chain.Vals.Proposer.Address, + } + hhash := tmHeader.Hash() + + blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) + + voteSet := tmtypes.NewVoteSet(chain.ChainID, chain.CurrentHeader.Height, 1, tmtypes.PrecommitType, chain.Vals) + + commit, err := tmtypes.MakeCommit(blockID, chain.CurrentHeader.Height, 1, voteSet, chain.Signers, chain.CurrentHeader.Time) + require.NoError(chain.t, err) + + signedHeader := tmtypes.SignedHeader{ + Header: &tmHeader, + Commit: commit, + } + + return ibctmtypes.Header{ + SignedHeader: signedHeader, + ValidatorSet: chain.Vals, + } +} + +// Copied unimported test functions from tmtypes to use them here +func MakeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.BlockID { + return tmtypes.BlockID{ + Hash: hash, + PartsHeader: tmtypes.PartSetHeader{ + Total: partSetSize, + Hash: partSetHash, + }, + } +} + // ConnectionOpenInit will construct and execute a MsgConnectionOpenInit. func (chain *TestChain) ConnectionOpenInit( counterparty *TestChain, - connection, counterpartyConnection TestConnection, + connection, counterpartyConnection *TestConnection, ) error { - prefix := commitmenttypes.NewMerklePrefix(counterparty.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix().Bytes()) - msg := connectiontypes.NewMsgConnectionOpenInit( connection.ID, connection.ClientID, counterpartyConnection.ID, connection.CounterpartyClientID, - prefix, + counterparty.GetPrefix(), chain.SenderAccount.GetAddress(), ) return chain.SendMsg(msg) @@ -282,21 +361,23 @@ func (chain *TestChain) ConnectionOpenInit( // ConnectionOpenTry will construct and execute a MsgConnectionOpenTry. func (chain *TestChain) ConnectionOpenTry( counterparty *TestChain, - connection, counterpartyConnection TestConnection, + connection, counterpartyConnection *TestConnection, ) error { - prefix := commitmenttypes.NewMerklePrefix(counterparty.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix().Bytes()) - connectionKey := host.KeyConnection(counterpartyConnection.ID) proofInit, proofHeight := counterparty.QueryProof(connectionKey) - consensusHeight := uint64(counterparty.App.LastBlockHeight()) - consensusKey := prefixedClientKey(connection.ClientID, host.KeyConsensusState(consensusHeight)) + // retrieve consensus state to provide proof for + consState, found := counterparty.App.IBCKeeper.ClientKeeper.GetLatestClientConsensusState(counterparty.GetContext(), counterpartyConnection.ClientID) + require.True(chain.t, found) + + consensusHeight := consState.GetHeight() + consensusKey := prefixedClientKey(counterpartyConnection.ClientID, host.KeyConsensusState(consensusHeight)) proofConsensus, _ := counterparty.QueryProof(consensusKey) msg := connectiontypes.NewMsgConnectionOpenTry( connection.ID, connection.ClientID, - counterpartyConnection.ID, connection.CounterpartyClientID, - prefix, []string{ConnectionVersion}, + counterpartyConnection.ID, counterpartyConnection.ClientID, + counterparty.GetPrefix(), []string{ConnectionVersion}, proofInit, proofConsensus, proofHeight, consensusHeight, chain.SenderAccount.GetAddress(), @@ -307,13 +388,17 @@ func (chain *TestChain) ConnectionOpenTry( // ConnectionOpenAck will construct and execute a MsgConnectionOpenAck. func (chain *TestChain) ConnectionOpenAck( counterparty *TestChain, - connection, counterpartyConnection TestConnection, + connection, counterpartyConnection *TestConnection, ) error { connectionKey := host.KeyConnection(counterpartyConnection.ID) proofTry, proofHeight := counterparty.QueryProof(connectionKey) - consensusHeight := uint64(counterparty.App.LastBlockHeight()) - consensusKey := prefixedClientKey(connection.ClientID, host.KeyConsensusState(consensusHeight)) + // retrieve consensus state to provide proof for + consState, found := counterparty.App.IBCKeeper.ClientKeeper.GetLatestClientConsensusState(counterparty.GetContext(), counterpartyConnection.ClientID) + require.True(chain.t, found) + + consensusHeight := consState.GetHeight() + consensusKey := prefixedClientKey(counterpartyConnection.ClientID, host.KeyConsensusState(consensusHeight)) proofConsensus, _ := counterparty.QueryProof(consensusKey) msg := connectiontypes.NewMsgConnectionOpenAck( @@ -329,7 +414,7 @@ func (chain *TestChain) ConnectionOpenAck( // ConnectionOpenConfirm will construct and execute a MsgConnectionOpenConfirm. func (chain *TestChain) ConnectionOpenConfirm( counterparty *TestChain, - connection, counterpartyConnection TestConnection, + connection, counterpartyConnection *TestConnection, ) error { connectionKey := host.KeyConnection(counterpartyConnection.ID) proof, height := counterparty.QueryProof(connectionKey) @@ -342,33 +427,87 @@ func (chain *TestChain) ConnectionOpenConfirm( return chain.SendMsg(msg) } -// ChannelOpenInit will construct and execute a MsgChannelOpenInit. -func (chain *TestChain) ChannelOpenInit( +// CreatePortCapability binds and claims a capability for the given portID if it does not +// already exist. This function will fail testing on any resulting error. +func (chain *TestChain) CreatePortCapability(portID string) { + // check if the portId is already binded, if not bind it + _, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), host.PortPath(portID)) + if !ok { + cap, err := chain.App.ScopedIBCKeeper.NewCapability(chain.GetContext(), host.PortPath(portID)) + require.NoError(chain.t, err) + err = chain.App.ScopedTransferKeeper.ClaimCapability(chain.GetContext(), cap, host.PortPath(portID)) + require.NoError(chain.t, err) + } + + chain.App.Commit() + + chain.NextBlock() +} + +// GetPortCapability returns the port capability for the given portID. The capability must +// exist, otherwise testing will fail. +func (chain *TestChain) GetPortCapability(portID string) *capabilitytypes.Capability { + cap, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), host.PortPath(portID)) + require.True(chain.t, ok) + + return cap +} + +// CreateChannelCapability binds and claims a capability for the given portID and channelID +// if it does not already exist. This function will fail testing on any resulting error. +func (chain *TestChain) CreateChannelCapability(portID, channelID string) { + capName := host.ChannelCapabilityPath(portID, channelID) + // check if the portId is already binded, if not bind it + _, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), capName) + if !ok { + cap, err := chain.App.ScopedIBCKeeper.NewCapability(chain.GetContext(), capName) + require.NoError(chain.t, err) + err = chain.App.ScopedTransferKeeper.ClaimCapability(chain.GetContext(), cap, capName) + require.NoError(chain.t, err) + } + + chain.App.Commit() + + chain.NextBlock() +} + +// GetChannelCapability returns the channel capability for the given portID and channelID. +// The capability must exist, otherwise testing will fail. +func (chain *TestChain) GetChannelCapability(portID, channelID string) *capabilitytypes.Capability { + cap, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), host.ChannelCapabilityPath(portID, channelID)) + require.True(chain.t, ok) + + return cap +} + +// ChanOpenInit will construct and execute a MsgChannelOpenInit. +func (chain *TestChain) ChanOpenInit( ch, counterparty TestChannel, order channeltypes.Order, connectionID string, ) error { msg := channeltypes.NewMsgChannelOpenInit( - ch.PortID, ch.ChannelID, + ch.PortID, ch.ID, ChannelVersion, order, []string{connectionID}, - counterparty.PortID, counterparty.ChannelID, + counterparty.PortID, counterparty.ID, chain.SenderAccount.GetAddress(), ) return chain.SendMsg(msg) } -// ChannelOpenTry will construct and execute a MsgChannelOpenTry. -func (chain *TestChain) ChannelOpenTry( - ch, counterparty TestChannel, +// ChanOpenTry will construct and execute a MsgChannelOpenTry. +func (chain *TestChain) ChanOpenTry( + counterparty *TestChain, + ch, counterpartyCh TestChannel, order channeltypes.Order, connectionID string, ) error { - proof, height := chain.QueryProof(host.KeyConnection(connectionID)) + proof, height := counterparty.QueryProof(host.KeyChannel(counterpartyCh.PortID, counterpartyCh.ID)) msg := channeltypes.NewMsgChannelOpenTry( - ch.PortID, ch.ChannelID, + ch.PortID, ch.ID, ChannelVersion, order, []string{connectionID}, - counterparty.PortID, counterparty.ChannelID, + counterpartyCh.PortID, counterpartyCh.ID, ChannelVersion, proof, height, chain.SenderAccount.GetAddress(), @@ -376,15 +515,15 @@ func (chain *TestChain) ChannelOpenTry( return chain.SendMsg(msg) } -// ChannelOpenAck will construct and execute a MsgChannelOpenAck. -func (chain *TestChain) ChannelOpenAck( - ch, counterparty TestChannel, - connectionID string, +// ChanOpenAck will construct and execute a MsgChannelOpenAck. +func (chain *TestChain) ChanOpenAck( + counterparty *TestChain, + ch, counterpartyCh TestChannel, ) error { - proof, height := chain.QueryProof(host.KeyConnection(connectionID)) + proof, height := counterparty.QueryProof(host.KeyChannel(counterpartyCh.PortID, counterpartyCh.ID)) msg := channeltypes.NewMsgChannelOpenAck( - ch.PortID, ch.ChannelID, + ch.PortID, ch.ID, ChannelVersion, proof, height, chain.SenderAccount.GetAddress(), @@ -392,17 +531,70 @@ func (chain *TestChain) ChannelOpenAck( return chain.SendMsg(msg) } -// ChannelOpenConfirm will construct and execute a MsgChannelOpenConfirm. -func (chain *TestChain) ChannelOpenConfirm( - ch, counterparty TestChannel, - connectionID string, +// ChanOpenConfirm will construct and execute a MsgChannelOpenConfirm. +func (chain *TestChain) ChanOpenConfirm( + counterparty *TestChain, + ch, counterpartyCh TestChannel, ) error { - proof, height := chain.QueryProof(host.KeyConnection(connectionID)) + proof, height := counterparty.QueryProof(host.KeyChannel(counterpartyCh.PortID, counterpartyCh.ID)) msg := channeltypes.NewMsgChannelOpenConfirm( - ch.PortID, ch.ChannelID, + ch.PortID, ch.ID, proof, height, chain.SenderAccount.GetAddress(), ) return chain.SendMsg(msg) } + +// ChanCloseInit will construct and execute a MsgChannelCloseInit. +// +// NOTE: does not work with ibc-transfer module +func (chain *TestChain) ChanCloseInit( + counterparty *TestChain, + channel TestChannel, +) error { + msg := channeltypes.NewMsgChannelCloseInit( + channel.PortID, channel.ID, + chain.SenderAccount.GetAddress(), + ) + return chain.SendMsg(msg) +} + +// SendPacket simulates sending a packet through the channel keeper. No message needs to be +// passed since this call is made from a module. +func (chain *TestChain) SendPacket( + packet channelexported.PacketI, +) error { + channelCap := chain.GetChannelCapability(packet.GetSourcePort(), packet.GetSourceChannel()) + + // no need to send message, acting as a module + err := chain.App.IBCKeeper.ChannelKeeper.SendPacket(chain.GetContext(), channelCap, packet) + if err != nil { + return err + } + + // commit changes + chain.App.Commit() + chain.NextBlock() + + return nil +} + +// PacketExecuted simulates receiving and wiritng an acknowledgement to the chain. +func (chain *TestChain) PacketExecuted( + packet channelexported.PacketI, +) error { + channelCap := chain.GetChannelCapability(packet.GetSourcePort(), packet.GetSourceChannel()) + + // no need to send message, acting as a handler + err := chain.App.IBCKeeper.ChannelKeeper.PacketExecuted(chain.GetContext(), channelCap, packet, TestHash) + if err != nil { + return err + } + + // commit changes + chain.App.Commit() + chain.NextBlock() + + return nil +} diff --git a/x/ibc/testing/coordinator.go b/x/ibc/testing/coordinator.go index 51040b3d8..cf1adcf13 100644 --- a/x/ibc/testing/coordinator.go +++ b/x/ibc/testing/coordinator.go @@ -2,6 +2,7 @@ package testing import ( "fmt" + "strconv" "testing" "time" @@ -9,7 +10,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" - connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + channelexported "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported" channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" ) @@ -32,7 +33,7 @@ func NewCoordinator(t *testing.T, n int) *Coordinator { chains := make(map[string]*TestChain) for i := 0; i < n; i++ { - chainID := ChainIDPrefix + string(i) + chainID := GetChainID(i) chains[chainID] = NewTestChain(t, chainID) } return &Coordinator{ @@ -41,60 +42,57 @@ func NewCoordinator(t *testing.T, n int) *Coordinator { } } -// IncrementTime iterates through all the TestChain's and increments their current header time -// by 5 seconds. -// -// CONTRACT: this function must be called after every commit on any TestChain. -func (coord *Coordinator) IncrementTime() { - for _, chain := range coord.Chains { - chain.CurrentHeader = abci.Header{ - Height: chain.CurrentHeader.Height, - Time: chain.CurrentHeader.Time.Add((timeIncrement)), - } - } +// Setup constructs a TM client, connection, and channel on both chains provided. It will +// fails if any error occurs. The clientID's, TestConnections, and TestChannels are returned +// for both chains. +func (coord *Coordinator) Setup( + chainA, chainB *TestChain, +) (string, string, *TestConnection, *TestConnection, TestChannel, TestChannel) { + clientA, clientB, connA, connB := coord.SetupClientConnections(chainA, chainB, clientexported.Tendermint) + + // channels can also be referenced through the returned connections + channelA, channelB := coord.CreateChannel(chainA, chainB, connA, connB, channeltypes.UNORDERED) + + return clientA, clientB, connA, connB, channelA, channelB } -// GetChain returns the TestChain using the given chainID and returns an error if it does -// not exist. -func (coord *Coordinator) GetChain(chainID string) *TestChain { - chain, found := coord.Chains[chainID] - require.True(coord.t, found, fmt.Sprintf("%s chain does not exist", chainID)) - return chain +// SetupClients is a helper function to create clients on both chains. It assumes the +// caller does not anticipate any errors. +func (coord *Coordinator) SetupClients( + chainA, chainB *TestChain, + clientType clientexported.ClientType, +) (string, string) { + + clientA, err := coord.CreateClient(chainA, chainB, clientType) + require.NoError(coord.t, err) + + clientB, err := coord.CreateClient(chainB, chainA, clientType) + require.NoError(coord.t, err) + + return clientA, clientB } -// CommitBlock commits a block on the provided indexes and then increments the global time. -// -// CONTRACT: the passed in list of indexes must not contain duplicates -func (coord *Coordinator) CommitBlock(chains ...string) { - for _, chainID := range chains { - chain := coord.GetChain(chainID) - chain.App.Commit() - chain.NextBlock() - } - coord.IncrementTime() -} +// SetupClientConnections is a helper function to create clients and the appropriate +// connections on both the source and counterparty chain. It assumes the caller does not +// anticipate any errors. +func (coord *Coordinator) SetupClientConnections( + chainA, chainB *TestChain, + clientType clientexported.ClientType, +) (string, string, *TestConnection, *TestConnection) { -// CommitNBlocks commits n blocks to state and updates the block height by 1 for each commit. -func (coord *Coordinator) CommitNBlocks(chainID string, n uint64) { - chain := coord.GetChain(chainID) + clientA, clientB := coord.SetupClients(chainA, chainB, clientType) - for i := uint64(0); i < n; i++ { - chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) - chain.App.Commit() - chain.NextBlock() - coord.IncrementTime() - } + connA, connB := coord.CreateConnection(chainA, chainB, clientA, clientB) + + return clientA, clientB, connA, connB } // CreateClient creates a counterparty client on the source chain and returns the clientID. func (coord *Coordinator) CreateClient( - sourceID, counterpartyID string, + source, counterparty *TestChain, clientType clientexported.ClientType, ) (clientID string, err error) { - coord.CommitBlock(sourceID, counterpartyID) - - source := coord.GetChain(sourceID) - counterparty := coord.GetChain(counterpartyID) + coord.CommitBlock(source, counterparty) clientID = source.NewClientID(counterparty.ChainID) @@ -117,14 +115,11 @@ func (coord *Coordinator) CreateClient( // UpdateClient updates a counterparty client on the source chain. func (coord *Coordinator) UpdateClient( - sourceID, counterpartyID, + source, counterparty *TestChain, clientID string, clientType clientexported.ClientType, ) (err error) { - coord.CommitBlock(sourceID, counterpartyID) - - source := coord.GetChain(sourceID) - counterparty := coord.GetChain(counterpartyID) + coord.CommitBlock(source, counterparty) switch clientType { case clientexported.Tendermint: @@ -144,58 +139,69 @@ func (coord *Coordinator) UpdateClient( } // CreateConnection constructs and executes connection handshake messages in order to create -// OPEN channels on source and counterparty chains. The connection information of the source -// and counterparty's are returned within a TestConnection struct. If there is a fault in -// the connection handshake then an error is returned. -// -// NOTE: The counterparty testing connection will be created even if it is not created in the -// application state. +// OPEN channels on chainA and chainB. The connection information of for chainA and chainB +// are returned within a TestConnection struct. The function expects the connections to be +// successfully opened otherwise testing will fail. func (coord *Coordinator) CreateConnection( - sourceID, counterpartyID, - clientID, counterpartyClientID string, - state connectiontypes.State, -) (TestConnection, TestConnection, error) { - source := coord.GetChain(sourceID) - counterparty := coord.GetChain(counterpartyID) + chainA, chainB *TestChain, + clientA, clientB string, +) (*TestConnection, *TestConnection) { - sourceConnection := source.NewTestConnection(clientID, counterpartyClientID) - counterpartyConnection := counterparty.NewTestConnection(counterpartyClientID, clientID) + connA, connB, err := coord.ConnOpenInit(chainA, chainB, clientA, clientB) + require.NoError(coord.t, err) - if err := coord.CreateConnectionInit(source, counterparty, sourceConnection, counterpartyConnection); err != nil { - return sourceConnection, counterpartyConnection, err - } + err = coord.ConnOpenTry(chainB, chainA, connB, connA) + require.NoError(coord.t, err) - if err := coord.CreateConnectionOpenTry(counterparty, source, counterpartyConnection, sourceConnection); err != nil { - return sourceConnection, counterpartyConnection, err - } + err = coord.ConnOpenAck(chainA, chainB, connA, connB) + require.NoError(coord.t, err) - if err := coord.CreateConnectionOpenAck(source, counterparty, sourceConnection, counterpartyConnection); err != nil { - return sourceConnection, counterpartyConnection, err - } + err = coord.ConnOpenConfirm(chainB, chainA, connB, connA) + require.NoError(coord.t, err) - if err := coord.CreateConnectionOpenConfirm(counterparty, source, counterpartyConnection, sourceConnection); err != nil { - return sourceConnection, counterpartyConnection, err - } - - return sourceConnection, counterpartyConnection, nil + return connA, connB } -// CreateConenctionInit initializes a connection on the source chain with the state INIT -// using the OpenInit handshake call. -func (coord *Coordinator) CreateConnectionInit( +// CreateChannel constructs and executes channel handshake messages in order to create +// OPEN channels on chainA and chainB. The function expects the channels to be successfully +// opened otherwise testing will fail. +func (coord *Coordinator) CreateChannel( + chainA, chainB *TestChain, + connA, connB *TestConnection, + order channeltypes.Order, +) (TestChannel, TestChannel) { + + channelA, channelB, err := coord.ChanOpenInit(chainA, chainB, connA, connB, order) + require.NoError(coord.t, err) + + err = coord.ChanOpenTry(chainB, chainA, channelB, channelA, connB, order) + require.NoError(coord.t, err) + + err = coord.ChanOpenAck(chainA, chainB, channelA, channelB) + require.NoError(coord.t, err) + + err = coord.ChanOpenConfirm(chainB, chainA, channelB, channelA) + require.NoError(coord.t, err) + + return channelA, channelB +} + +// SendPacket sends a packet through the channel keeper on the source chain and updates the +// counterparty client for the source chain. +func (coord *Coordinator) SendPacket( source, counterparty *TestChain, - sourceConnection, counterpartyConnection TestConnection, + packet channelexported.PacketI, + counterpartyClientID string, ) error { - // initialize connection on source - if err := source.ConnectionOpenInit(counterparty, sourceConnection, counterpartyConnection); err != nil { + if err := source.SendPacket(packet); err != nil { return err } coord.IncrementTime() // update source client on counterparty connection if err := coord.UpdateClient( - counterparty.ChainID, source.ChainID, - counterpartyConnection.ClientID, clientexported.Tendermint, + counterparty, source, + counterpartyClientID, clientexported.Tendermint, ); err != nil { return err } @@ -203,11 +209,108 @@ func (coord *Coordinator) CreateConnectionInit( return nil } -// CreateConenctionOpenTry initializes a connection on the source chain with the state TRYOPEN -// using the OpenTry handshake call. -func (coord *Coordinator) CreateConnectionOpenTry( +// PacketExecuted receives a packet through the channel keeper on the source chain and updates the +// counterparty client for the source chain. +func (coord *Coordinator) PacketExecuted( source, counterparty *TestChain, - sourceConnection, counterpartyConnection TestConnection, + packet channelexported.PacketI, + counterpartyClientID string, +) error { + if err := source.PacketExecuted(packet); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + if err := coord.UpdateClient( + counterparty, source, + counterpartyClientID, clientexported.Tendermint, + ); err != nil { + return err + } + + return nil +} + +// IncrementTime iterates through all the TestChain's and increments their current header time +// by 5 seconds. +// +// CONTRACT: this function must be called after every commit on any TestChain. +func (coord *Coordinator) IncrementTime() { + for _, chain := range coord.Chains { + chain.CurrentHeader.Time = chain.CurrentHeader.Time.Add(timeIncrement) + chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) + } +} + +// GetChain returns the TestChain using the given chainID and returns an error if it does +// not exist. +func (coord *Coordinator) GetChain(chainID string) *TestChain { + chain, found := coord.Chains[chainID] + require.True(coord.t, found, fmt.Sprintf("%s chain does not exist", chainID)) + return chain +} + +// GetChainID returns the chainID used for the provided index. +func GetChainID(index int) string { + return ChainIDPrefix + strconv.Itoa(index) +} + +// CommitBlock commits a block on the provided indexes and then increments the global time. +// +// CONTRACT: the passed in list of indexes must not contain duplicates +func (coord *Coordinator) CommitBlock(chains ...*TestChain) { + for _, chain := range chains { + chain.App.Commit() + chain.NextBlock() + } + coord.IncrementTime() +} + +// CommitNBlocks commits n blocks to state and updates the block height by 1 for each commit. +func (coord *Coordinator) CommitNBlocks(chain *TestChain, n uint64) { + for i := uint64(0); i < n; i++ { + chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) + chain.App.Commit() + chain.NextBlock() + coord.IncrementTime() + } +} + +// ConnOpenInit initializes a connection on the source chain with the state INIT +// using the OpenInit handshake call. +// +// NOTE: The counterparty testing connection will be created even if it is not created in the +// application state. +func (coord *Coordinator) ConnOpenInit( + source, counterparty *TestChain, + clientID, counterpartyClientID string, +) (*TestConnection, *TestConnection, error) { + sourceConnection := source.NewTestConnection(clientID, counterpartyClientID) + counterpartyConnection := counterparty.NewTestConnection(counterpartyClientID, clientID) + + // initialize connection on source + if err := source.ConnectionOpenInit(counterparty, sourceConnection, counterpartyConnection); err != nil { + return sourceConnection, counterpartyConnection, err + } + coord.IncrementTime() + + // update source client on counterparty connection + if err := coord.UpdateClient( + counterparty, source, + counterpartyClientID, clientexported.Tendermint, + ); err != nil { + return sourceConnection, counterpartyConnection, err + } + + return sourceConnection, counterpartyConnection, nil +} + +// ConnOpenTry initializes a connection on the source chain with the state TRYOPEN +// using the OpenTry handshake call. +func (coord *Coordinator) ConnOpenTry( + source, counterparty *TestChain, + sourceConnection, counterpartyConnection *TestConnection, ) error { // initialize TRYOPEN connection on source if err := source.ConnectionOpenTry(counterparty, sourceConnection, counterpartyConnection); err != nil { @@ -217,7 +320,7 @@ func (coord *Coordinator) CreateConnectionOpenTry( // update source client on counterparty connection if err := coord.UpdateClient( - counterparty.ChainID, source.ChainID, + counterparty, source, counterpartyConnection.ClientID, clientexported.Tendermint, ); err != nil { return err @@ -226,11 +329,11 @@ func (coord *Coordinator) CreateConnectionOpenTry( return nil } -// CreateConnectionOpenAck initializes a connection on the source chain with the state OPEN +// ConnOpenAck initializes a connection on the source chain with the state OPEN // using the OpenAck handshake call. -func (coord *Coordinator) CreateConnectionOpenAck( +func (coord *Coordinator) ConnOpenAck( source, counterparty *TestChain, - sourceConnection, counterpartyConnection TestConnection, + sourceConnection, counterpartyConnection *TestConnection, ) error { // set OPEN connection on source using OpenAck if err := source.ConnectionOpenAck(counterparty, sourceConnection, counterpartyConnection); err != nil { @@ -240,7 +343,7 @@ func (coord *Coordinator) CreateConnectionOpenAck( // update source client on counterparty connection if err := coord.UpdateClient( - counterparty.ChainID, source.ChainID, + counterparty, source, counterpartyConnection.ClientID, clientexported.Tendermint, ); err != nil { return err @@ -249,21 +352,21 @@ func (coord *Coordinator) CreateConnectionOpenAck( return nil } -// CreateConnectionOpenConfirm initializes a connection on the source chain with the state OPEN +// ConnOpenConfirm initializes a connection on the source chain with the state OPEN // using the OpenConfirm handshake call. -func (coord *Coordinator) CreateConnectionOpenConfirm( +func (coord *Coordinator) ConnOpenConfirm( source, counterparty *TestChain, - sourceConnection, counterpartyConnection TestConnection, + sourceConnection, counterpartyConnection *TestConnection, ) error { - if err := counterparty.ConnectionOpenConfirm(counterparty, sourceConnection, counterpartyConnection); err != nil { + if err := source.ConnectionOpenConfirm(counterparty, sourceConnection, counterpartyConnection); err != nil { return err } coord.IncrementTime() // update source client on counterparty connection if err := coord.UpdateClient( - source.ChainID, counterparty.ChainID, - sourceConnection.ClientID, clientexported.Tendermint, + counterparty, source, + counterpartyConnection.ClientID, clientexported.Tendermint, ); err != nil { return err } @@ -271,87 +374,58 @@ func (coord *Coordinator) CreateConnectionOpenConfirm( return nil } -// CreateChannel constructs and executes channel handshake messages in order to create -// channels on source and counterparty chains with the passed in Channel State. The portID and -// channelID of source and counterparty are returned. +// ChanOpenInit initializes a channel on the source chain with the state INIT +// using the OpenInit handshake call. // // NOTE: The counterparty testing channel will be created even if it is not created in the // application state. -func (coord *Coordinator) CreateChannel( - sourceID, counterpartyID string, - connection, counterpartyConnection TestConnection, +func (coord *Coordinator) ChanOpenInit( + source, counterparty *TestChain, + connection, counterpartyConnection *TestConnection, order channeltypes.Order, - state channeltypes.State, ) (TestChannel, TestChannel, error) { - source := coord.GetChain(sourceID) - counterparty := coord.GetChain(counterpartyID) + sourceChannel := connection.AddTestChannel() + counterpartyChannel := counterpartyConnection.AddTestChannel() - sourceChannel := source.NewTestChannel() - counterpartyChannel := counterparty.NewTestChannel() + // create port capability + source.CreatePortCapability(sourceChannel.PortID) + coord.IncrementTime() - if err := coord.CreateChannelInit(source, counterparty, sourceChannel, counterpartyChannel, connection, order); err != nil { + // initialize channel on source + if err := source.ChanOpenInit(sourceChannel, counterpartyChannel, order, connection.ID); err != nil { return sourceChannel, counterpartyChannel, err } + coord.IncrementTime() - if err := coord.CreateChannelOpenTry(counterparty, source, counterpartyChannel, sourceChannel, counterpartyConnection, order); err != nil { - return sourceChannel, counterpartyChannel, err - } - - if err := coord.CreateChannelOpenAck(source, counterparty, sourceChannel, counterpartyChannel, connection); err != nil { - return sourceChannel, counterpartyChannel, err - } - - if err := coord.CreateChannelOpenConfirm(counterparty, source, counterpartyChannel, sourceChannel, counterpartyConnection); err != nil { + // update source client on counterparty connection + if err := coord.UpdateClient( + counterparty, source, + counterpartyConnection.ClientID, clientexported.Tendermint, + ); err != nil { return sourceChannel, counterpartyChannel, err } return sourceChannel, counterpartyChannel, nil } -// CreateChannelInit initializes a channel on the source chain with the state INIT -// using the OpenInit handshake call. -func (coord *Coordinator) CreateChannelInit( - source, counterparty *TestChain, - sourceChannel, counterpartyChannel TestChannel, - connection TestConnection, - order channeltypes.Order, -) error { - - // initialize channel on source - if err := source.ChannelOpenInit(sourceChannel, counterpartyChannel, order, connection.ID); err != nil { - return err - } - coord.IncrementTime() - - // update source client on counterparty connection - if err := coord.UpdateClient( - counterparty.ChainID, source.ChainID, - connection.CounterpartyClientID, clientexported.Tendermint, - ); err != nil { - return err - } - - return nil -} - -// CreateChannelOpenTry initializes a channel on the source chain with the state TRYOPEN +// ChanOpenTry initializes a channel on the source chain with the state TRYOPEN // using the OpenTry handshake call. -func (coord *Coordinator) CreateChannelOpenTry( +func (coord *Coordinator) ChanOpenTry( source, counterparty *TestChain, sourceChannel, counterpartyChannel TestChannel, - connection TestConnection, + connection *TestConnection, order channeltypes.Order, ) error { // initialize channel on source - if err := source.ChannelOpenTry(sourceChannel, counterpartyChannel, order, connection.ID); err != nil { + if err := source.ChanOpenTry(counterparty, sourceChannel, counterpartyChannel, order, connection.ID); err != nil { return err } coord.IncrementTime() // update source client on counterparty connection if err := coord.UpdateClient( - counterparty.ChainID, source.ChainID, + counterparty, source, connection.CounterpartyClientID, clientexported.Tendermint, ); err != nil { return err @@ -360,24 +434,22 @@ func (coord *Coordinator) CreateChannelOpenTry( return nil } -// CreateChannelOpenAck initializes a channel on the source chain with the state OPEN +// ChanOpenAck initializes a channel on the source chain with the state OPEN // using the OpenAck handshake call. -func (coord *Coordinator) CreateChannelOpenAck( +func (coord *Coordinator) ChanOpenAck( source, counterparty *TestChain, sourceChannel, counterpartyChannel TestChannel, - connection TestConnection, ) error { - // initialize channel on source - if err := source.ChannelOpenAck(sourceChannel, counterpartyChannel, connection.ID); err != nil { + if err := source.ChanOpenAck(counterparty, sourceChannel, counterpartyChannel); err != nil { return err } coord.IncrementTime() // update source client on counterparty connection if err := coord.UpdateClient( - counterparty.ChainID, source.ChainID, - connection.CounterpartyClientID, clientexported.Tendermint, + counterparty, source, + sourceChannel.CounterpartyClientID, clientexported.Tendermint, ); err != nil { return err } @@ -385,24 +457,70 @@ func (coord *Coordinator) CreateChannelOpenAck( return nil } -// CreateChannelOpenConfirm initializes a channel on the source chain with the state OPEN +// ChanOpenConfirm initializes a channel on the source chain with the state OPEN // using the OpenConfirm handshake call. -func (coord *Coordinator) CreateChannelOpenConfirm( +func (coord *Coordinator) ChanOpenConfirm( source, counterparty *TestChain, sourceChannel, counterpartyChannel TestChannel, - connection TestConnection, ) error { - // initialize channel on source - if err := source.ChannelOpenConfirm(sourceChannel, counterpartyChannel, connection.ID); err != nil { + if err := source.ChanOpenConfirm(counterparty, sourceChannel, counterpartyChannel); err != nil { return err } coord.IncrementTime() // update source client on counterparty connection if err := coord.UpdateClient( - counterparty.ChainID, source.ChainID, - connection.CounterpartyClientID, clientexported.Tendermint, + counterparty, source, + sourceChannel.CounterpartyClientID, clientexported.Tendermint, + ); err != nil { + return err + } + + return nil +} + +// ChanCloseInit closes a channel on the source chain resulting in the channels state +// being set to CLOSED. +// +// NOTE: does not work with ibc-transfer module +func (coord *Coordinator) ChanCloseInit( + source, counterparty *TestChain, + channel TestChannel, +) error { + + if err := source.ChanCloseInit(counterparty, channel); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + if err := coord.UpdateClient( + counterparty, source, + channel.CounterpartyClientID, clientexported.Tendermint, + ); err != nil { + return err + } + + return nil +} + +// SetChannelClosed sets a channel state to CLOSED. +func (coord *Coordinator) SetChannelClosed( + source, counterparty *TestChain, + testChannel TestChannel, +) error { + channel := source.GetChannel(testChannel) + + channel.State = channeltypes.CLOSED + source.App.IBCKeeper.ChannelKeeper.SetChannel(source.GetContext(), testChannel.PortID, testChannel.ID, channel) + + coord.CommitBlock(source) + + // update source client on counterparty connection + if err := coord.UpdateClient( + counterparty, source, + testChannel.CounterpartyClientID, clientexported.Tendermint, ); err != nil { return err } diff --git a/x/ibc/testing/types.go b/x/ibc/testing/types.go index 672a484c5..2704c0006 100644 --- a/x/ibc/testing/types.go +++ b/x/ibc/testing/types.go @@ -1,16 +1,62 @@ package testing +import ( + "fmt" +) + // TestConnections is a testing helper struct to keep track of the connectionID, source clientID, // and counterparty clientID used in creating and interacting with a connection. type TestConnection struct { ID string ClientID string CounterpartyClientID string + Channels []TestChannel +} + +// AddTestChannel appends a new TestChannel which contains references to the port and channel ID +// used for channel creation and interaction. +// +// channel ID format: connectionid- +// the port is set to "transfer" to be compatible with the ICS-transfer module, this should +// eventually be updated as described in the issue: https://github.com/cosmos/cosmos-sdk/issues/6509 +func (conn *TestConnection) AddTestChannel() TestChannel { + channel := conn.NextTestChannel() + conn.Channels = append(conn.Channels, channel) + return channel +} + +// NextTestChannel returns the next test channel to be created on this connection, but does not +// add it to the list of created channels. This function is expected to be used when the caller +// has not created the associated channel in app state, but would still like to refer to the +// non-existent channel usually to test for its non-existence. +func (conn *TestConnection) NextTestChannel() TestChannel { + portID := "transfer" + channelID := fmt.Sprintf("%s-%d", conn.ID, len(conn.Channels)) + return TestChannel{ + PortID: portID, + ID: channelID, + ClientID: conn.ClientID, + CounterpartyClientID: conn.CounterpartyClientID, + } +} + +// FirstOrNextTestChannel returns the first test channel if it exists, otherwise it +// returns the next test channel to be created. This function is expected to be used +// when the caller does not know if the channel has or has not been created in app +// state, but would still like to refer to it to test existence or non-existence. +func (conn *TestConnection) FirstOrNextTestChannel() TestChannel { + if len(conn.Channels) > 0 { + return conn.Channels[0] + } + return conn.NextTestChannel() } // TestChannel is a testing helper struct to keep track of the portID and channelID -// used in creating and interacting with a channel. +// used in creating and interacting with a channel. The clientID and counterparty +// client ID are also tracked to cut down on querying and argument passing. type TestChannel struct { - PortID string - ChannelID string + PortID string + ID string + ClientID string + CounterpartyClientID string }