diff --git a/tests/e2e/channel_init.go b/tests/e2e/channel_init.go index f1c82f2d0e..9785ef0c39 100644 --- a/tests/e2e/channel_init.go +++ b/tests/e2e/channel_init.go @@ -37,7 +37,7 @@ func (suite *CCVTestSuite) TestConsumerGenesis() { _, ok = suite.consumerApp.GetIBCKeeper().ClientKeeper.GetClientState(ctx, clientId) suite.Require().True(ok) - suite.SetupCCVChannel() + suite.SetupCCVChannel(suite.path) origTime := suite.consumerChain.GetContext().BlockTime() diff --git a/tests/e2e/common.go b/tests/e2e/common.go index ac48b3e63f..ffd0e5473c 100644 --- a/tests/e2e/common.go +++ b/tests/e2e/common.go @@ -6,19 +6,18 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/interchain-security/testutil/e2e" - providertypes "github.com/cosmos/interchain-security/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/x/ccv/types" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/interchain-security/testutil/e2e" + icstestingutils "github.com/cosmos/interchain-security/testutil/ibc_testing" + providertypes "github.com/cosmos/interchain-security/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/x/ccv/types" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" ) // ChainType defines the type of chain (either provider or consumer) @@ -29,10 +28,16 @@ const ( Consumer ) +// firstConsumerBundle returns the bundle of the first consumer chain +func (s *CCVTestSuite) getFirstBundle() icstestingutils.ConsumerBundle { + return *s.consumerBundles[icstestingutils.FirstConsumerChainID] +} + func (s *CCVTestSuite) providerCtx() sdk.Context { return s.providerChain.GetContext() } +// consumerCtx returns the context of only the FIRST consumer chain func (s *CCVTestSuite) consumerCtx() sdk.Context { return s.consumerChain.GetContext() } @@ -199,6 +204,7 @@ func relayAllCommittedPackets( // - get packets packet, found := srcChain.GetSentPacket(commitment.Sequence, srcChannelID) s.Require().True(found, "did not find sent packet") + // - relay the packet err := path.RelayPacket(packet) s.Require().NoError(err) @@ -425,7 +431,7 @@ func (suite *CCVTestSuite) CreateCustomClient(endpoint *ibctesting.Endpoint, unb height := endpoint.Counterparty.Chain.LastHeader.GetHeight().(clienttypes.Height) UpgradePath := []string{"upgrade", "upgradedIBCState"} - clientState := ibctmtypes.NewClientState( + clientState := ibctm.NewClientState( endpoint.Counterparty.Chain.ChainID, tmConfig.TrustLevel, tmConfig.TrustingPeriod, tmConfig.UnbondingPeriod, tmConfig.MaxClockDrift, height, commitmenttypes.GetSDKSpecs(), UpgradePath, tmConfig.AllowUpdateAfterExpiry, tmConfig.AllowUpdateAfterMisbehaviour, ) @@ -443,17 +449,22 @@ func (suite *CCVTestSuite) CreateCustomClient(endpoint *ibctesting.Endpoint, unb require.NoError(endpoint.Chain.T, err) } -func (suite *CCVTestSuite) GetConsumerEndpointClientAndConsState() (exported.ClientState, exported.ConsensusState) { +// GetConsumerEndpointClientAndConsState returns the client and consensus state +// for a particular consumer endpoint, as specified by the consumer's bundle. +func (suite *CCVTestSuite) GetConsumerEndpointClientAndConsState( + consumerBundle icstestingutils.ConsumerBundle) (exported.ClientState, exported.ConsensusState) { + + ctx := consumerBundle.GetCtx() + consumerKeeper := consumerBundle.GetKeeper() - clientID, found := suite.consumerApp.GetConsumerKeeper().GetProviderClientID(suite.consumerCtx()) + clientID, found := consumerKeeper.GetProviderClientID(ctx) suite.Require().True(found) - clientState, found := suite.consumerApp.GetIBCKeeper().ClientKeeper.GetClientState( - suite.consumerCtx(), clientID) + clientState, found := consumerBundle.App.GetIBCKeeper().ClientKeeper.GetClientState(ctx, clientID) suite.Require().True(found) - consState, found := suite.consumerApp.GetIBCKeeper().ClientKeeper.GetClientConsensusState( - suite.consumerCtx(), clientID, clientState.GetLatestHeight()) + consState, found := consumerBundle.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState( + ctx, clientID, clientState.GetLatestHeight()) suite.Require().True(found) return clientState, consState diff --git a/tests/e2e/distribution.go b/tests/e2e/distribution.go index cffae78e54..7d9121f0f7 100644 --- a/tests/e2e/distribution.go +++ b/tests/e2e/distribution.go @@ -14,7 +14,7 @@ import ( func (s *CCVTestSuite) TestRewardsDistribution() { //set up channel and delegate some tokens in order for validator set update to be sent to the consumer chain - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) s.SetupTransferChannel() bondAmt := sdk.NewInt(10000000) delAddr := s.providerChain.SenderAccount.GetAddress() diff --git a/tests/e2e/expired_client.go b/tests/e2e/expired_client.go index 579df936ab..7b5e4093ff 100644 --- a/tests/e2e/expired_client.go +++ b/tests/e2e/expired_client.go @@ -20,7 +20,7 @@ import ( func (s *CCVTestSuite) TestVSCPacketSendExpiredClient() { providerKeeper := s.providerApp.GetProviderKeeper() - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) expireClient(s, Consumer) @@ -85,7 +85,7 @@ func (s *CCVTestSuite) TestConsumerPacketSendExpiredClient() { providerKeeper := s.providerApp.GetProviderKeeper() consumerKeeper := s.consumerApp.GetConsumerKeeper() - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) // bond some tokens on provider to change validator powers bondAmt := sdk.NewInt(1000000) diff --git a/tests/e2e/setup.go b/tests/e2e/setup.go index 643f8e5ded..5aff0578fc 100644 --- a/tests/e2e/setup.go +++ b/tests/e2e/setup.go @@ -6,8 +6,8 @@ import ( ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" e2eutil "github.com/cosmos/interchain-security/testutil/e2e" - icstestingutils "github.com/cosmos/interchain-security/testutil/ibc_testing" + icstestingutils "github.com/cosmos/interchain-security/testutil/ibc_testing" ccv "github.com/cosmos/interchain-security/x/ccv/types" "github.com/cosmos/interchain-security/x/ccv/utils" @@ -27,14 +27,24 @@ import ( type CCVTestSuite struct { suite.Suite coordinator *ibctesting.Coordinator + setupCallback SetupCallback + providerChain *ibctesting.TestChain - consumerChain *ibctesting.TestChain providerApp e2eutil.ProviderApp - consumerApp e2eutil.ConsumerApp - path *ibctesting.Path - transferPath *ibctesting.Path - setupCallback SetupCallback - skippedTests map[string]bool + + // The first consumer chain among multiple. + consumerChain *ibctesting.TestChain + // The first consumer app among multiple. + consumerApp e2eutil.ConsumerApp + // The ccv path to the first consumer among multiple. + path *ibctesting.Path + // The transfer path to the first consumer among multiple. + transferPath *ibctesting.Path + + // A map from consumer chain ID to its consumer bundle. + // The preferred way to access chains, apps, and paths when designing tests around multiple consumers. + consumerBundles map[string]*icstestingutils.ConsumerBundle + skippedTests map[string]bool } // NewCCVTestSuite returns a new instance of CCVTestSuite, ready to be tested against using suite.Run(). @@ -42,13 +52,13 @@ func NewCCVTestSuite[Tp e2eutil.ProviderApp, Tc e2eutil.ConsumerApp]( providerAppIniter ibctesting.AppIniter, consumerAppIniter ibctesting.AppIniter, skippedTests []string) *CCVTestSuite { ccvSuite := new(CCVTestSuite) - + + // Define callback called before each test. ccvSuite.setupCallback = func(t *testing.T) ( *ibctesting.Coordinator, *ibctesting.TestChain, - *ibctesting.TestChain, e2eutil.ProviderApp, - e2eutil.ConsumerApp, + map[string]*icstestingutils.ConsumerBundle, ) { // Instantiate the test coordinator. coordinator := ibctesting.NewCoordinator(t, 0) @@ -58,14 +68,15 @@ func NewCCVTestSuite[Tp e2eutil.ProviderApp, Tc e2eutil.ConsumerApp]( provider, providerApp := icstestingutils.AddProvider[Tp]( coordinator, t, providerAppIniter) + numConsumers := 5 + // Add specified number of consumers to coordinator, store returned test chains and apps. // Concrete consumer app type is passed to the generic function here. - consumers, consumerApps := icstestingutils.AddConsumers[Tc]( - coordinator, t, 1, consumerAppIniter) + consumerBundles := icstestingutils.AddConsumers[Tc]( + coordinator, t, numConsumers, consumerAppIniter) // Pass variables to suite. - // TODO: accept multiple consumers here - return coordinator, provider, consumers[0], providerApp, consumerApps[0] + return coordinator, provider, providerApp, consumerBundles } ccvSuite.skippedTests = make(map[string]bool) @@ -80,9 +91,8 @@ func NewCCVTestSuite[Tp e2eutil.ProviderApp, Tc e2eutil.ConsumerApp]( type SetupCallback func(t *testing.T) ( coord *ibctesting.Coordinator, providerChain *ibctesting.TestChain, - consumerChain *ibctesting.TestChain, providerApp e2eutil.ProviderApp, - consumerApp e2eutil.ConsumerApp, + consumerBundles map[string]*icstestingutils.ConsumerBundle, ) func (suite *CCVTestSuite) BeforeTest(suiteName, testName string) { @@ -96,115 +106,143 @@ func (suite *CCVTestSuite) SetupTest() { // Instantiate new test utils using callback suite.coordinator, suite.providerChain, - suite.consumerChain, suite.providerApp, - suite.consumerApp = suite.setupCallback(suite.T()) + suite.providerApp, suite.consumerBundles = suite.setupCallback(suite.T()) + + // valsets must match between provider and all consumers + for _, bundle := range suite.consumerBundles { + + providerValUpdates := tmtypes.TM2PB.ValidatorUpdates(suite.providerChain.Vals) + consumerValUpdates := tmtypes.TM2PB.ValidatorUpdates(bundle.Chain.Vals) + suite.Require().True(len(providerValUpdates) == len(consumerValUpdates), "initial valset not matching") + for i := 0; i < len(providerValUpdates); i++ { + addr1 := utils.GetChangePubKeyAddress(providerValUpdates[i]) + addr2 := utils.GetChangePubKeyAddress(consumerValUpdates[i]) + suite.Require().True(bytes.Equal(addr1, addr2), "validator mismatch") + } + // Move each consumer to next block + bundle.Chain.NextBlock() + } + + // move provider to next block + suite.providerChain.NextBlock() providerKeeper := suite.providerApp.GetProviderKeeper() - consumerKeeper := suite.consumerApp.GetConsumerKeeper() - - // valsets must match - providerValUpdates := tmtypes.TM2PB.ValidatorUpdates(suite.providerChain.Vals) - consumerValUpdates := tmtypes.TM2PB.ValidatorUpdates(suite.consumerChain.Vals) - suite.Require().True(len(providerValUpdates) == len(consumerValUpdates), "initial valset not matching") - for i := 0; i < len(providerValUpdates); i++ { - addr1 := utils.GetChangePubKeyAddress(providerValUpdates[i]) - addr2 := utils.GetChangePubKeyAddress(consumerValUpdates[i]) - suite.Require().True(bytes.Equal(addr1, addr2), "validator mismatch") + + for chainID, bundle := range suite.consumerBundles { + // For each consumer, create client to that consumer on the provider chain. + err := providerKeeper.CreateConsumerClient( + suite.providerCtx(), + chainID, + bundle.Chain.LastHeader.GetHeight().(clienttypes.Height), + false, + ) + suite.Require().NoError(err) } - // move both chains to the next block - suite.providerChain.NextBlock() - suite.consumerChain.NextBlock() - - // create consumer client on provider chain and set as consumer client for consumer chainID in provider keeper. - err := providerKeeper.CreateConsumerClient( - suite.providerCtx(), - suite.consumerChain.ChainID, - suite.consumerChain.LastHeader.GetHeight().(clienttypes.Height), - false, - ) - suite.Require().NoError(err) // move provider to next block to commit the state suite.providerChain.NextBlock() - // initialize the consumer chain with the genesis state stored on the provider - consumerGenesisState, found := providerKeeper.GetConsumerGenesis( - suite.providerCtx(), - suite.consumerChain.ChainID, - ) - suite.Require().True(found, "consumer genesis not found") - consumerKeeper.InitGenesis(suite.consumerCtx(), &consumerGenesisState) - - // Confirm client and cons state for consumer were set correctly in InitGenesis - consumerEndpointClientState, consumerEndpointConsState := suite.GetConsumerEndpointClientAndConsState() - suite.Require().Equal(consumerGenesisState.ProviderClientState, consumerEndpointClientState) - suite.Require().Equal(consumerGenesisState.ProviderConsensusState, consumerEndpointConsState) + // initialize each consumer chain with it's corresponding genesis state + // stored on the provider. + for chainID, bundle := range suite.consumerBundles { + + consumerGenesisState, found := providerKeeper.GetConsumerGenesis( + suite.providerCtx(), + chainID, + ) + suite.Require().True(found, "consumer genesis not found") + + consumerKeeper := bundle.GetKeeper() + consumerKeeper.InitGenesis(bundle.GetCtx(), &consumerGenesisState) + + // Confirm client and cons state for consumer were set correctly in InitGenesis + consumerEndpointClientState, + consumerEndpointConsState := suite.GetConsumerEndpointClientAndConsState(*bundle) + suite.Require().Equal(consumerGenesisState.ProviderClientState, consumerEndpointClientState) + suite.Require().Equal(consumerGenesisState.ProviderConsensusState, consumerEndpointConsState) + + // create path for the CCV channel + bundle.Path = ibctesting.NewPath(bundle.Chain, suite.providerChain) + + // Set provider endpoint's clientID for each consumer + providerEndpointClientID, found := providerKeeper.GetConsumerClientId( + suite.providerCtx(), + chainID, + ) + suite.Require().True(found, "provider endpoint clientID not found") + bundle.Path.EndpointB.ClientID = providerEndpointClientID + + // Set consumer endpoint's clientID + consumerKeeper = bundle.GetKeeper() + consumerEndpointClientID, found := consumerKeeper.GetProviderClientID(bundle.GetCtx()) + suite.Require().True(found, "consumer endpoint clientID not found") + bundle.Path.EndpointA.ClientID = consumerEndpointClientID + + // Note: suite.path.EndpointA.ClientConfig and suite.path.EndpointB.ClientConfig are not populated, + // since these IBC testing package fields are unused in our tests. + + // Confirm client config is now correct + suite.ValidateEndpointsClientConfig(*bundle) + + // - channel config + bundle.Path.EndpointA.ChannelConfig.PortID = ccv.ConsumerPortID + bundle.Path.EndpointB.ChannelConfig.PortID = ccv.ProviderPortID + bundle.Path.EndpointA.ChannelConfig.Version = ccv.Version + bundle.Path.EndpointB.ChannelConfig.Version = ccv.Version + bundle.Path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED + bundle.Path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED + + // create path for the transfer channel + bundle.TransferPath = ibctesting.NewPath(bundle.Chain, suite.providerChain) + bundle.TransferPath.EndpointA.ChannelConfig.PortID = transfertypes.PortID + bundle.TransferPath.EndpointB.ChannelConfig.PortID = transfertypes.PortID + bundle.TransferPath.EndpointA.ChannelConfig.Version = transfertypes.Version + bundle.TransferPath.EndpointB.ChannelConfig.Version = transfertypes.Version + } - // create path for the CCV channel - suite.path = ibctesting.NewPath(suite.consumerChain, suite.providerChain) + // Support tests that were written before multiple consumers were supported. + firstBundle := suite.getFirstBundle() + suite.consumerApp = firstBundle.App + suite.consumerChain = firstBundle.Chain + suite.path = firstBundle.Path + suite.transferPath = firstBundle.TransferPath +} - // Set provider endpoint's clientID - providerEndpointClientID, found := providerKeeper.GetConsumerClientId( - suite.providerCtx(), - suite.consumerChain.ChainID, - ) - suite.Require().True(found, "provider endpoint clientID not found") - suite.path.EndpointB.ClientID = providerEndpointClientID - - // Set consumer endpoint's clientID - consumerEndpointClientID, found := consumerKeeper.GetProviderClientID(suite.consumerChain.GetContext()) - suite.Require().True(found, "consumer endpoint clientID not found") - suite.path.EndpointA.ClientID = consumerEndpointClientID - - // Note: suite.path.EndpointA.ClientConfig and suite.path.EndpointB.ClientConfig are not populated, - // since these IBC testing package fields are unused in our tests. - - // Confirm client config is now correct - suite.ValidateEndpointsClientConfig() - - // - channel config - suite.path.EndpointA.ChannelConfig.PortID = ccv.ConsumerPortID - suite.path.EndpointB.ChannelConfig.PortID = ccv.ProviderPortID - suite.path.EndpointA.ChannelConfig.Version = ccv.Version - suite.path.EndpointB.ChannelConfig.Version = ccv.Version - suite.path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED - suite.path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED - - // create path for the transfer channel - suite.transferPath = ibctesting.NewPath(suite.consumerChain, suite.providerChain) - suite.transferPath.EndpointA.ChannelConfig.PortID = transfertypes.PortID - suite.transferPath.EndpointB.ChannelConfig.PortID = transfertypes.PortID - suite.transferPath.EndpointA.ChannelConfig.Version = transfertypes.Version - suite.transferPath.EndpointB.ChannelConfig.Version = transfertypes.Version +func (suite *CCVTestSuite) SetupAllCCVChannels() { + for _, bundle := range suite.consumerBundles { + suite.SetupCCVChannel(bundle.Path) + } } -func (suite *CCVTestSuite) SetupCCVChannel() { - suite.StartSetupCCVChannel() - suite.CompleteSetupCCVChannel() +func (suite *CCVTestSuite) SetupCCVChannel(path *ibctesting.Path) { + suite.StartSetupCCVChannel(path) + suite.CompleteSetupCCVChannel(path) } -func (suite *CCVTestSuite) StartSetupCCVChannel() { - suite.coordinator.CreateConnections(suite.path) +func (suite *CCVTestSuite) StartSetupCCVChannel(path *ibctesting.Path) { + suite.coordinator.CreateConnections(path) - err := suite.path.EndpointA.ChanOpenInit() + err := path.EndpointA.ChanOpenInit() suite.Require().NoError(err) - err = suite.path.EndpointB.ChanOpenTry() + err = path.EndpointB.ChanOpenTry() suite.Require().NoError(err) } -func (suite *CCVTestSuite) CompleteSetupCCVChannel() { - err := suite.path.EndpointA.ChanOpenAck() +func (suite *CCVTestSuite) CompleteSetupCCVChannel(path *ibctesting.Path) { + err := path.EndpointA.ChanOpenAck() suite.Require().NoError(err) - err = suite.path.EndpointB.ChanOpenConfirm() + err = path.EndpointB.ChanOpenConfirm() suite.Require().NoError(err) // ensure counterparty is up to date - err = suite.path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) } +// TODO: Make SetupTransferChannel functional for multiple consumers by pattern matching SetupCCVChannel. +// See: https://github.com/cosmos/interchain-security/issues/506 func (suite *CCVTestSuite) SetupTransferChannel() { // transfer path will use the same connection as ccv path @@ -234,12 +272,13 @@ func (suite *CCVTestSuite) SetupTransferChannel() { suite.Require().NoError(err) } -func (s CCVTestSuite) ValidateEndpointsClientConfig() { - consumerKeeper := s.consumerApp.GetConsumerKeeper() +func (s CCVTestSuite) ValidateEndpointsClientConfig(consumerBundle icstestingutils.ConsumerBundle) { + consumerKeeper := consumerBundle.GetKeeper() providerStakingKeeper := s.providerApp.GetStakingKeeper() - consumerUnbondingPeriod := consumerKeeper.GetUnbondingPeriod(s.consumerCtx()) - cs, ok := s.providerApp.GetIBCKeeper().ClientKeeper.GetClientState(s.providerCtx(), s.path.EndpointB.ClientID) + consumerUnbondingPeriod := consumerKeeper.GetUnbondingPeriod(consumerBundle.GetCtx()) + cs, ok := s.providerApp.GetIBCKeeper().ClientKeeper.GetClientState(s.providerCtx(), + consumerBundle.Path.EndpointB.ClientID) s.Require().True(ok) s.Require().Equal( consumerUnbondingPeriod, @@ -248,7 +287,8 @@ func (s CCVTestSuite) ValidateEndpointsClientConfig() { ) providerUnbondingPeriod := providerStakingKeeper.UnbondingTime(s.providerCtx()) - cs, ok = s.consumerApp.GetIBCKeeper().ClientKeeper.GetClientState(s.consumerCtx(), s.path.EndpointA.ClientID) + cs, ok = consumerBundle.App.GetIBCKeeper().ClientKeeper.GetClientState( + consumerBundle.GetCtx(), consumerBundle.Path.EndpointA.ClientID) s.Require().True(ok) s.Require().Equal( providerUnbondingPeriod, diff --git a/tests/e2e/slashing.go b/tests/e2e/slashing.go index 87cd0f658a..1345b4ef72 100644 --- a/tests/e2e/slashing.go +++ b/tests/e2e/slashing.go @@ -39,15 +39,18 @@ func (s *CCVTestSuite) TestRelayAndApplySlashPacket() { for _, tc := range testCases { + // Reset test state s.SetupTest() - s.SetupCCVChannel() - s.SetupTransferChannel() + + // Setup CCV channel for all instantiated consumers + s.SetupAllCCVChannels() + validatorsPerChain := len(s.consumerChain.Vals.Validators) providerStakingKeeper := s.providerApp.GetE2eStakingKeeper() providerSlashingKeeper := s.providerApp.GetE2eSlashingKeeper() providerKeeper := s.providerApp.GetProviderKeeper() - consumerKeeper := s.consumerApp.GetConsumerKeeper() + firstConsumerKeeper := s.getFirstBundle().GetKeeper() // get a cross-chain validator address, pubkey and balance tmVals := s.consumerChain.Vals.Validators @@ -67,8 +70,8 @@ func (s *CCVTestSuite) TestRelayAndApplySlashPacket() { s.providerCtx().BlockHeight()-1, time.Time{}.UTC(), false, int64(0)) providerSlashingKeeper.SetValidatorSigningInfo(s.providerCtx(), consAddr, valInfo) - // get valseUpdateId for current block height - valsetUpdateId := consumerKeeper.GetHeightValsetUpdateID( + // get valseUpdateId for current block height on first consumer + valsetUpdateId := firstConsumerKeeper.GetHeightValsetUpdateID( s.consumerCtx(), uint64(s.consumerCtx().BlockHeight())) // construct the slash packet with the validator address and power @@ -92,55 +95,61 @@ func (s *CCVTestSuite) TestRelayAndApplySlashPacket() { packet := channeltypes.NewPacket(packetData, 1, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, ccv.ProviderPortID, s.path.EndpointB.ChannelID, clienttypes.Height{}, timeout) - // Send the slash packet through CCV - err = s.path.EndpointA.SendPacket(packet) + // Send slash packet from the first consumer chain + err = s.getFirstBundle().Path.EndpointA.SendPacket(packet) s.Require().NoError(err) if tc == downtimeTestCase { - // Set outstanding slashing flag if testing a downtime slash packet - consumerKeeper.SetOutstandingDowntime(s.consumerCtx(), consAddr) + // Set outstanding slashing flag for first consumer if testing a downtime slash packet + firstConsumerKeeper.SetOutstandingDowntime(s.consumerCtx(), consAddr) } - // Note: RecvPacket advances two blocks. Let's say the provider is currently at height n. - // The received slash packet will be handled during n, and the staking module will then - // register a validator update from that packet during the endblocker of n. Then the ccv - // module sends a VSC packet during the endblocker of n. The new validator set will be - // committed to in block n+1, and will be in effect for block n+2. + // Note: RecvPacket advances two blocks. Let's say the provider is currently at height N. + // The received slash packet will be handled during N, and the staking module will then + // register a validator update from that packet during the endblocker of N. Then the ccv + // module sends VSC packets during the endblocker of N. The new validator set will be + // committed to in block N+1, and will be in effect for block N+2. valsetUpdateN := providerKeeper.GetValidatorSetUpdateId(s.providerCtx()) - // receive the downtime packet on the provider chain. RecvPacket() calls the provider endblocker twice + // receive the downtime packet on the provider chain. + // RecvPacket() calls the provider endblocker twice err = s.path.EndpointB.RecvPacket(packet) s.Require().NoError(err) // We've now advanced two blocks. - // One VSC packet should have been sent during block n + // VSC packets should have been sent from provider during block N to each consumer expectedSentValsetUpdateId := valsetUpdateN - _, found = providerKeeper.GetVscSendTimestamp(s.providerCtx(), - s.consumerChain.ChainID, expectedSentValsetUpdateId) - s.Require().True(found) + for _, bundle := range s.consumerBundles { + _, found = providerKeeper.GetVscSendTimestamp(s.providerCtx(), + bundle.Chain.ChainID, expectedSentValsetUpdateId) + s.Require().True(found) + } - // Confirm the valset update Id was incremented twice. + // Confirm the valset update Id was incremented twice on provider, + // since two endblockers have passed. valsetUpdateNPlus2 := providerKeeper.GetValidatorSetUpdateId(s.providerCtx()) s.Require().Equal(valsetUpdateN+2, valsetUpdateNPlus2) // check that the validator was removed from the provider validator set s.Require().Len(s.providerChain.Vals.Validators, validatorsPerChain-1) - // Relay the VSC packet to the consumer - relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 1) + for _, bundle := range s.consumerBundles { + // Relay VSC packets from provider to each consumer + relayAllCommittedPackets(s, s.providerChain, bundle.Path, + ccv.ProviderPortID, bundle.Path.EndpointB.ChannelID, 1) - // check that the consumer updated its VSC ID for the subsequent block - actualValsetUpdateID := consumerKeeper.GetHeightValsetUpdateID( - s.consumerCtx(), uint64(s.consumerCtx().BlockHeight())+1) - s.Require().Equal(expectedSentValsetUpdateId, actualValsetUpdateID) + // check that each consumer updated its VSC ID for the subsequent block + consumerKeeper := bundle.GetKeeper() + ctx := bundle.GetCtx() + actualValsetUpdateID := consumerKeeper.GetHeightValsetUpdateID( + ctx, uint64(ctx.BlockHeight())+1) + s.Require().Equal(expectedSentValsetUpdateId, actualValsetUpdateID) - // check that the validator was removed from the consumer validator set - s.Require().Len(s.consumerChain.Vals.Validators, validatorsPerChain-1) - - err = s.path.EndpointB.UpdateClient() - s.Require().NoError(err) + // check that slashed validator was removed from each consumer validator set + s.Require().Len(bundle.Chain.Vals.Validators, validatorsPerChain-1) + } // check that the validator is successfully jailed on provider validatorJailed, ok := providerStakingKeeper.GetValidatorByConsAddr(s.providerCtx(), consAddr) @@ -148,7 +157,7 @@ func (s *CCVTestSuite) TestRelayAndApplySlashPacket() { s.Require().True(validatorJailed.Jailed) s.Require().Equal(validatorJailed.Status, stakingtypes.Unbonding) - // check that the validator's tokens were slashed + // check that the slashed validator's tokens were indeed slashed on provider var slashFraction sdk.Dec if tc == downtimeTestCase { slashFraction = providerSlashingKeeper.SlashFractionDowntime(s.providerCtx()) @@ -161,23 +170,24 @@ func (s *CCVTestSuite) TestRelayAndApplySlashPacket() { resultingTokens := valOldBalance.Sub(slashedAmount.TruncateInt()) s.Require().Equal(resultingTokens, validatorJailed.GetTokens()) - // check that the validator's unjailing time is updated + // check that the validator's unjailing time is updated on provider valSignInfo, found := providerSlashingKeeper.GetValidatorSigningInfo(s.providerCtx(), consAddr) s.Require().True(found) s.Require().True(valSignInfo.JailedUntil.After(s.providerCtx().BlockHeader().Time)) if tc == downtimeTestCase { - // check that the outstanding slashing flag is reset on the consumer - pFlag := consumerKeeper.OutstandingDowntime(s.consumerCtx(), consAddr) + // check that the outstanding slashing flag is reset on first consumer, + // since that consumer originally sent the slash packet + pFlag := firstConsumerKeeper.OutstandingDowntime(s.consumerCtx(), consAddr) s.Require().False(pFlag) - // check that slashing packet gets acknowledged + // check that slashing packet gets acknowledged successfully ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) err = s.path.EndpointA.AcknowledgePacket(packet, ack.Acknowledgement()) s.Require().NoError(err) } else if tc == doubleSignTestCase { - // check that validator was tombstoned + // check that validator was tombstoned on provider s.Require().True(valSignInfo.Tombstoned) s.Require().True(valSignInfo.JailedUntil.Equal(evidencetypes.DoubleSignJailEndTime)) } @@ -188,7 +198,7 @@ func (s *CCVTestSuite) TestSlashPacketAcknowledgement() { providerKeeper := s.providerApp.GetProviderKeeper() consumerKeeper := s.consumerApp.GetConsumerKeeper() - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) s.SetupTransferChannel() packet := channeltypes.NewPacket([]byte{}, 1, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, @@ -435,7 +445,7 @@ func (suite *CCVTestSuite) TestHandleSlashPacketDistribution() { // when a validator has downtime on the slashing module func (suite *CCVTestSuite) TestValidatorDowntime() { // initial setup - suite.SetupCCVChannel() + suite.SetupCCVChannel(suite.path) suite.SendEmptyVSCPacket() consumerKeeper := suite.consumerApp.GetConsumerKeeper() @@ -536,7 +546,7 @@ func (suite *CCVTestSuite) TestValidatorDowntime() { // when a double-signing evidence is handled by the evidence module func (suite *CCVTestSuite) TestValidatorDoubleSigning() { // initial setup - suite.SetupCCVChannel() + suite.SetupCCVChannel(suite.path) suite.SendEmptyVSCPacket() // sync suite context after CCV channel is established @@ -605,7 +615,7 @@ func (suite *CCVTestSuite) TestValidatorDoubleSigning() { // TestQueueAndSendSlashPacket tests the integration of QueueSlashPacket with SendPackets. // In normal operation slash packets are queued in BeginBlock and sent in EndBlock. func (suite *CCVTestSuite) TestQueueAndSendSlashPacket() { - suite.SetupCCVChannel() + suite.SetupCCVChannel(suite.path) consumerKeeper := suite.consumerApp.GetConsumerKeeper() consumerIBCKeeper := suite.consumerApp.GetIBCKeeper() diff --git a/tests/e2e/stop_consumer.go b/tests/e2e/stop_consumer.go index d8007a3d7b..4267e16fd2 100644 --- a/tests/e2e/stop_consumer.go +++ b/tests/e2e/stop_consumer.go @@ -48,7 +48,7 @@ func (s *CCVTestSuite) TestStopConsumerChain() { }{ { func(suite *CCVTestSuite) error { - suite.SetupCCVChannel() + suite.SetupCCVChannel(s.path) suite.SetupTransferChannel() return nil }, @@ -99,7 +99,7 @@ func (s *CCVTestSuite) TestStopConsumerChain() { // TODO Simon: implement OnChanCloseConfirm in IBC-GO testing to close the consumer chain's channel end func (s *CCVTestSuite) TestStopConsumerOnChannelClosed() { // init the CCV channel states - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) s.SetupTransferChannel() s.SendEmptyVSCPacket() @@ -179,7 +179,7 @@ func (s *CCVTestSuite) checkConsumerChainIsRemoved(chainID string, lockUbd bool, // when the provider channel was established and then closed func (suite *CCVTestSuite) TestProviderChannelClosed() { - suite.SetupCCVChannel() + suite.SetupCCVChannel(suite.path) // establish provider channel with a first VSC packet suite.SendEmptyVSCPacket() diff --git a/tests/e2e/unbonding.go b/tests/e2e/unbonding.go index cbd34025f5..2a3b3199eb 100644 --- a/tests/e2e/unbonding.go +++ b/tests/e2e/unbonding.go @@ -79,7 +79,7 @@ func (s *CCVTestSuite) TestUndelegationNormalOperation() { consumerKeeper := s.consumerApp.GetConsumerKeeper() stakingKeeper := s.providerApp.GetE2eStakingKeeper() - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) // set VSC timeout period to not trigger the removal of the consumer chain providerUnbondingPeriod := stakingKeeper.UnbondingTime(s.providerCtx()) @@ -129,7 +129,7 @@ func (s *CCVTestSuite) TestUndelegationNormalOperation() { func (s *CCVTestSuite) TestUndelegationVscTimeout() { providerKeeper := s.providerApp.GetProviderKeeper() - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) // set VSC timeout period to trigger the removal of the consumer chain vscTimeout := providerKeeper.GetVscTimeoutPeriod(s.providerCtx()) @@ -263,7 +263,7 @@ func (s *CCVTestSuite) TestUndelegationDuringInit() { ) // complete CCV channel setup - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) // relay VSC packets from provider to consumer relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 2) @@ -304,8 +304,10 @@ func (s *CCVTestSuite) TestUnbondingNoConsumer() { providerKeeper := s.providerApp.GetProviderKeeper() providerStakingKeeper := s.providerApp.GetE2eStakingKeeper() - // remove the consumer chain, which was already registered during setup - providerKeeper.DeleteConsumerClientId(s.providerCtx(), s.consumerChain.ChainID) + // remove all consumer chains, which were already registered during setup + for chainID := range s.consumerBundles { + providerKeeper.DeleteConsumerClientId(s.providerCtx(), chainID) + } // delegate bondAmt and undelegate 1/2 of it bondAmt := sdk.NewInt(10000000) @@ -379,7 +381,7 @@ func (s *CCVTestSuite) TestRedelegationNoConsumer() { // TestRedelegationWithConsumer tests a redelegate transaction submitted on a provider chain // when the unbonding period elapses first on the provider chain func (s *CCVTestSuite) TestRedelegationProviderFirst() { - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) s.SetupTransferChannel() providerKeeper := s.providerApp.GetProviderKeeper() diff --git a/tests/e2e/valset_update.go b/tests/e2e/valset_update.go index 072e943d2a..4b250eaaaa 100644 --- a/tests/e2e/valset_update.go +++ b/tests/e2e/valset_update.go @@ -13,7 +13,7 @@ import ( // TestPacketRoundtrip tests a CCV packet roundtrip when tokens are bonded on provider func (s *CCVTestSuite) TestPacketRoundtrip() { - s.SetupCCVChannel() + s.SetupCCVChannel(s.path) s.SetupTransferChannel() // Bond some tokens on provider to change validator powers @@ -41,7 +41,7 @@ func (suite *CCVTestSuite) TestQueueAndSendVSCMaturedPackets() { consumerKeeper := suite.consumerApp.GetConsumerKeeper() // setup CCV channel - suite.SetupCCVChannel() + suite.SetupCCVChannel(suite.path) // send 3 packets to consumer chain at different times pk, err := cryptocodec.FromTmPubKeyInterface(suite.providerChain.Vals.Validators[0].PubKey) diff --git a/testutil/ibc_testing/generic_setup.go b/testutil/ibc_testing/generic_setup.go index 7c3234437d..bb96ca387f 100644 --- a/testutil/ibc_testing/generic_setup.go +++ b/testutil/ibc_testing/generic_setup.go @@ -3,8 +3,10 @@ package ibc_testing import ( "testing" + sdk "github.com/cosmos/cosmos-sdk/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/cosmos/interchain-security/testutil/e2e" + e2eutil "github.com/cosmos/interchain-security/testutil/e2e" + consumerkeeper "github.com/cosmos/interchain-security/x/ccv/consumer/keeper" ) // Contains generic setup code for running e2e tests against a provider, consumer, @@ -12,12 +14,32 @@ import ( // to run e2e tests against your app.go implementations! var ( + FirstConsumerChainID = ibctesting.GetChainID(2) provChainID = ibctesting.GetChainID(1) - democConsumerChainID = ibctesting.GetChainID(2) + democConsumerChainID = ibctesting.GetChainID(5000) ) +// ConsumerBundle serves as a way to store useful in-mem consumer app chain state +// and relevant IBC paths in the context of CCV e2e testing. +type ConsumerBundle struct { + Chain *ibctesting.TestChain + App e2eutil.ConsumerApp + Path *ibctesting.Path + TransferPath *ibctesting.Path +} + +// GetCtx returns the context for the ConsumerBundle +func (cb ConsumerBundle) GetCtx() sdk.Context { + return cb.Chain.GetContext() +} + +// GetKeeper returns the keeper for the ConsumerBundle +func (cb ConsumerBundle) GetKeeper() consumerkeeper.Keeper { + return cb.App.GetConsumerKeeper() +} + // AddProvider adds a new provider chain to the coordinator and returns the test chain and app type -func AddProvider[T e2e.ProviderApp](coordinator *ibctesting.Coordinator, t *testing.T, appIniter ibctesting.AppIniter) ( +func AddProvider[T e2eutil.ProviderApp](coordinator *ibctesting.Coordinator, t *testing.T, appIniter ibctesting.AppIniter) ( *ibctesting.TestChain, T) { provider := ibctesting.NewTestChain(t, coordinator, appIniter, provChainID) @@ -27,7 +49,7 @@ func AddProvider[T e2e.ProviderApp](coordinator *ibctesting.Coordinator, t *test } // AddDemocracyConsumer adds a new democ consumer chain to the coordinator and returns the test chain and app type -func AddDemocracyConsumer[T e2e.DemocConsumerApp](coordinator *ibctesting.Coordinator, t *testing.T, +func AddDemocracyConsumer[T e2eutil.DemocConsumerApp](coordinator *ibctesting.Coordinator, t *testing.T, appIniter ibctesting.AppIniter) (*ibctesting.TestChain, T) { democConsumer := ibctesting.NewTestChain(t, coordinator, appIniter, democConsumerChainID) @@ -40,23 +62,23 @@ func AddDemocracyConsumer[T e2e.DemocConsumerApp](coordinator *ibctesting.Coordi // The new consumers are initialized with the valset of the existing provider chain. // // This method must be called after AddProvider. -func AddConsumers[T e2e.ConsumerApp](coordinator *ibctesting.Coordinator, - t *testing.T, numConsumers int, appIniter ibctesting.AppIniter) ([]*ibctesting.TestChain, []T) { +func AddConsumers[T e2eutil.ConsumerApp](coordinator *ibctesting.Coordinator, + t *testing.T, numConsumers int, appIniter ibctesting.AppIniter) map[string]*ConsumerBundle { providerChain := coordinator.GetChain(provChainID) - // Instantiate specified number of consumers, add them to coordinator, and returned chain/app slices - consumerChains := []*ibctesting.TestChain{} - consumerApps := []T{} + // Instantiate specified number of consumer bundles, add each consumer chain to coordinator + consumerBundles := make(map[string]*ConsumerBundle) for i := 0; i < numConsumers; i++ { chainID := ibctesting.GetChainID(i + 2) testChain := ibctesting.NewTestChainWithValSet(t, coordinator, appIniter, chainID, providerChain.Vals, providerChain.Signers) coordinator.Chains[chainID] = testChain - consumerChains = append(consumerChains, testChain) - consumerApps = append(consumerApps, testChain.App.(T)) + consumerBundles[chainID] = &ConsumerBundle{ + Chain: testChain, + App: testChain.App.(T), + } } - - return consumerChains, consumerApps + return consumerBundles }