diff --git a/app/modules.go b/app/modules.go index ecac3b126a9..d51acab9206 100644 --- a/app/modules.go +++ b/app/modules.go @@ -17,7 +17,6 @@ import ( ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" no_valupdates_genutil "github.com/cosmos/interchain-security/v5/x/ccv/no_valupdates_genutil" no_valupdates_staking "github.com/cosmos/interchain-security/v5/x/ccv/no_valupdates_staking" - icsproviderclient "github.com/cosmos/interchain-security/v5/x/ccv/provider/client" providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" "cosmossdk.io/x/evidence" @@ -138,10 +137,6 @@ func newBasicManagerFromManager(app *GaiaApp) module.BasicManager { govtypes.ModuleName: gov.NewAppModuleBasic( []govclient.ProposalHandler{ paramsclient.ProposalHandler, - icsproviderclient.ConsumerAdditionProposalHandler, - icsproviderclient.ConsumerRemovalProposalHandler, - icsproviderclient.ConsumerModificationProposalHandler, - icsproviderclient.ChangeRewardDenomsProposalHandler, }, ), }) diff --git a/app/upgrades/v20/upgrades.go b/app/upgrades/v20/upgrades.go index 54b84b83da3..cf8ead61492 100644 --- a/app/upgrades/v20/upgrades.go +++ b/app/upgrades/v20/upgrades.go @@ -4,13 +4,16 @@ import ( "context" providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" errorsmod "cosmossdk.io/errors" upgradetypes "cosmossdk.io/x/upgrade/types" + codec "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/gaia/v20/app/keepers" @@ -62,6 +65,12 @@ func CreateUpgradeHandler( return vm, errorsmod.Wrapf(err, "initializing LastProviderConsensusValSet during migration") } + ctx.Logger().Info("Migrating ICS legacy proposals...") + err = MigrateICSLegacyProposals(ctx, *keepers.GovKeeper) + if err != nil { + return vm, errorsmod.Wrapf(err, "migrating ICS legacy proposals during migration") + } + ctx.Logger().Info("Upgrade v20 complete") return vm, nil } @@ -110,7 +119,7 @@ func InitializeLastProviderConsensusValidatorSet( } // create consensus validators for the staking validators - lastValidators := []types.ConsensusValidator{} + lastValidators := []providertypes.ConsensusValidator{} for _, val := range vals { consensusVal, err := providerKeeper.CreateProviderConsensusValidator(ctx, val) if err != nil { @@ -123,3 +132,135 @@ func InitializeLastProviderConsensusValidatorSet( providerKeeper.SetLastProviderConsensusValSet(ctx, lastValidators) return nil } + +// MigrateICSLegacyProposals migrates ICS legacy proposals +func MigrateICSLegacyProposals(ctx sdk.Context, govKeeper govkeeper.Keeper) error { + return govKeeper.Proposals.Walk(ctx, nil, func(key uint64, proposal govtypes.Proposal) (stop bool, err error) { + err = MigrateProposal(ctx, govKeeper, proposal) + if err != nil { + return true, errorsmod.Wrapf(err, "migrating proposal %d", key) + } + return false, nil + }) +} + +// MigrateProposal migrates a proposal by converting legacy messages to new messages. +func MigrateProposal(ctx sdk.Context, govKeeper govkeeper.Keeper, proposal govtypes.Proposal) error { + for idx, msg := range proposal.GetMessages() { + sdkLegacyMsg, isLegacyProposal := msg.GetCachedValue().(*govtypes.MsgExecLegacyContent) + if !isLegacyProposal { + continue + } + content, err := govtypes.LegacyContentFromMessage(sdkLegacyMsg) + if err != nil { + continue + } + + msgAdd, ok := content.(*providertypes.ConsumerAdditionProposal) + if ok { + anyMsg, err := MigrateLegacyConsumerAddition(*msgAdd, govKeeper.GetAuthority()) + if err != nil { + return err + } + proposal.Messages[idx] = anyMsg + continue // skip the rest of the loop + } + + msgRemove, ok := content.(*providertypes.ConsumerRemovalProposal) + if ok { + anyMsg, err := MigrateLegacyConsumerRemoval(*msgRemove, govKeeper.GetAuthority()) + if err != nil { + return err + } + proposal.Messages[idx] = anyMsg + continue // skip the rest of the loop + } + + msgMod, ok := content.(*providertypes.ConsumerModificationProposal) + if ok { + anyMsg, err := MigrateConsumerModificationProposal(*msgMod, govKeeper.GetAuthority()) + if err != nil { + return err + } + proposal.Messages[idx] = anyMsg + continue // skip the rest of the loop + } + + msgChangeRewardDenoms, ok := content.(*providertypes.ChangeRewardDenomsProposal) + if ok { + anyMsg, err := MigrateChangeRewardDenomsProposal(*msgChangeRewardDenoms, govKeeper.GetAuthority()) + if err != nil { + return err + } + proposal.Messages[idx] = anyMsg + continue // skip the rest of the loop + } + } + return govKeeper.SetProposal(ctx, proposal) +} + +// MigrateLegacyConsumerAddition converts a ConsumerAdditionProposal to a MsgConsumerAdditionProposal +// and returns it as `Any` suitable to replace the legacy message. +// `authority` contains the signer address +func MigrateLegacyConsumerAddition(msg providertypes.ConsumerAdditionProposal, authority string) (*codec.Any, error) { + sdkMsg := providertypes.MsgConsumerAddition{ + ChainId: msg.ChainId, + InitialHeight: msg.InitialHeight, + GenesisHash: msg.GenesisHash, + BinaryHash: msg.BinaryHash, + SpawnTime: msg.SpawnTime, + UnbondingPeriod: msg.UnbondingPeriod, + CcvTimeoutPeriod: msg.CcvTimeoutPeriod, + TransferTimeoutPeriod: msg.TransferTimeoutPeriod, + ConsumerRedistributionFraction: msg.ConsumerRedistributionFraction, + BlocksPerDistributionTransmission: msg.BlocksPerDistributionTransmission, + HistoricalEntries: msg.HistoricalEntries, + DistributionTransmissionChannel: msg.DistributionTransmissionChannel, + Top_N: msg.Top_N, + ValidatorsPowerCap: msg.ValidatorsPowerCap, + ValidatorSetCap: msg.ValidatorSetCap, + Allowlist: msg.Allowlist, + Denylist: msg.Denylist, + Authority: authority, + MinStake: msg.MinStake, + AllowInactiveVals: msg.AllowInactiveVals, + } + return codec.NewAnyWithValue(&sdkMsg) +} + +// MigrateLegacyConsumerRemoval converts a ConsumerRemovalProposal to a MsgConsumerRemovalProposal +// and returns it as `Any` suitable to replace the legacy message. +// `authority` contains the signer address +func MigrateLegacyConsumerRemoval(msg providertypes.ConsumerRemovalProposal, authority string) (*codec.Any, error) { + sdkMsg := providertypes.MsgConsumerRemoval{ + ChainId: msg.ChainId, + StopTime: msg.StopTime, + Authority: authority, + } + return codec.NewAnyWithValue(&sdkMsg) +} + +// MigrateConsumerModificationProposal converts a ConsumerModificationProposal to a MsgConsumerModificationProposal +// and returns it as `Any` suitable to replace the legacy message. +// `authority` contains the signer address +func MigrateConsumerModificationProposal(msg providertypes.ConsumerModificationProposal, authority string) (*codec.Any, error) { + sdkMsg := providertypes.MsgConsumerModification{ + ChainId: msg.ChainId, + Allowlist: msg.Allowlist, + Denylist: msg.Denylist, + Authority: authority, + } + return codec.NewAnyWithValue(&sdkMsg) +} + +// MigrateChangeRewardDenomsProposal converts a ChangeRewardDenomsProposal to a MigrateChangeRewardDenomsProposal +// and returns it as `Any` suitable to replace the legacy message. +// `authority` contains the signer address +func MigrateChangeRewardDenomsProposal(msg providertypes.ChangeRewardDenomsProposal, authority string) (*codec.Any, error) { + sdkMsg := providertypes.MsgChangeRewardDenoms{ + DenomsToAdd: msg.GetDenomsToAdd(), + DenomsToRemove: msg.GetDenomsToRemove(), + Authority: authority, + } + return codec.NewAnyWithValue(&sdkMsg) +} diff --git a/go.mod b/go.mod index 447b1aad81e..703ff4413f7 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/cosmos/ibc-apps/modules/rate-limiting/v8 v8.0.0 github.com/cosmos/ibc-go/modules/capability v1.0.0 github.com/cosmos/ibc-go/v8 v8.4.0 - github.com/cosmos/interchain-security/v5 v5.0.0-20240806104629-29327696b8e6 + github.com/cosmos/interchain-security/v5 v5.0.0-20240823135732-3a0c55f65769 github.com/google/gofuzz v1.2.0 github.com/gorilla/mux v1.8.1 github.com/ory/dockertest/v3 v3.11.0 diff --git a/go.sum b/go.sum index a8253435bef..5be0cd2ab88 100644 --- a/go.sum +++ b/go.sum @@ -443,6 +443,8 @@ github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZD github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= github.com/cosmos/interchain-security/v5 v5.0.0-20240806104629-29327696b8e6 h1:aFwnbEdeMUaQg9ZU+Pm8fE3CzZM2gG5jSeAvYOd+hoU= github.com/cosmos/interchain-security/v5 v5.0.0-20240806104629-29327696b8e6/go.mod h1:sT6a/OIwwkXuH9fBzt5IBa4lrlWO8etgQ+b59pIE8k4= +github.com/cosmos/interchain-security/v5 v5.0.0-20240823135732-3a0c55f65769 h1:R1ncQp/CwgV/u2fWebEWdp/o3IiINb3RuWEg4ATrEGQ= +github.com/cosmos/interchain-security/v5 v5.0.0-20240823135732-3a0c55f65769/go.mod h1:y3LdR1GPxF8SMFRb/V38OWGZNwEriJDFlka/hoH1GEk= github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo= github.com/cosmos/keyring v1.2.0/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= diff --git a/tests/e2e/e2e_gov_test.go b/tests/e2e/e2e_gov_test.go index 5b5705e695d..fc6b57d7e74 100644 --- a/tests/e2e/e2e_gov_test.go +++ b/tests/e2e/e2e_gov_test.go @@ -136,20 +136,6 @@ func (s *IntegrationTestSuite) GovCommunityPoolSpend() { ) } -func (s *IntegrationTestSuite) submitLegacyGovProposal(chainAAPIEndpoint, sender string, proposalID int, proposalType string, submitFlags []string, depositFlags []string, voteFlags []string, voteCommand string, withDeposit bool) { - s.T().Logf("Submitting Gov Proposal: %s", proposalType) - // min deposit of 1000uatom is required in e2e tests, otherwise the gov antehandler causes the proposal to be dropped - sflags := submitFlags - if withDeposit { - sflags = append(sflags, "--deposit=1000uatom") - } - s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, "submit-legacy-proposal", sflags, govtypesv1beta1.StatusDepositPeriod) - s.T().Logf("Depositing Gov Proposal: %s", proposalType) - s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, "deposit", depositFlags, govtypesv1beta1.StatusVotingPeriod) - s.T().Logf("Voting Gov Proposal: %s", proposalType) - s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, voteCommand, voteFlags, govtypesv1beta1.StatusPassed) -} - // NOTE: in SDK >= v0.47 the submit-proposal does not have a --deposit flag // Instead, the depoist is added to the "deposit" field of the proposal JSON (usually stored as a file) // you can use `gaiad tx gov draft-proposal` to create a proposal file that you can use diff --git a/tests/e2e/e2e_ics_test.go b/tests/e2e/e2e_ics_test.go deleted file mode 100644 index 6b41e20eb31..00000000000 --- a/tests/e2e/e2e_ics_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package e2e - -import ( - "encoding/json" - "fmt" - "path/filepath" - "strconv" - "strings" - "time" - - ibcclienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" -) - -const ( - proposalAddConsumerChainFilename = "proposal_add_consumer.json" - proposalRemoveConsumerChainFilename = "proposal_remove_consumer.json" -) - -type ConsumerAdditionProposalWithDeposit struct { - providertypes.ConsumerAdditionProposal - Deposit string `json:"deposit"` - Summary string `json:"summary"` // required on legacy proposals -} - -type ConsumerRemovalProposalWithDeposit struct { - providertypes.ConsumerRemovalProposal - Deposit string `json:"deposit"` - Summary string `json:"summary"` // required on legacy proposals -} - -func (s *IntegrationTestSuite) writeAddRemoveConsumerProposals(c *chain, consumerChainID string) { - hash, _ := json.Marshal("Z2VuX2hhc2g=") - addProp := &providertypes.ConsumerAdditionProposal{ - Title: "Create consumer chain", - Description: "First consumer chain", - ChainId: consumerChainID, - InitialHeight: ibcclienttypes.Height{ - RevisionHeight: 1, - }, - GenesisHash: hash, - BinaryHash: hash, - SpawnTime: time.Now(), - UnbondingPeriod: time.Duration(100000000000), - CcvTimeoutPeriod: time.Duration(100000000000), - TransferTimeoutPeriod: time.Duration(100000000000), - ConsumerRedistributionFraction: "0.75", - BlocksPerDistributionTransmission: 10, - HistoricalEntries: 10000, - Top_N: 95, - } - addPropWithDeposit := ConsumerAdditionProposalWithDeposit{ - ConsumerAdditionProposal: *addProp, - Deposit: "1000uatom", - // Summary is - Summary: "Summary for the First consumer chain addition proposal", - } - - removeProp := &providertypes.ConsumerRemovalProposal{ - Title: "Remove consumer chain", - Description: "Removing consumer chain", - ChainId: consumerChainID, - StopTime: time.Now(), - } - - removePropWithDeposit := ConsumerRemovalProposalWithDeposit{ - ConsumerRemovalProposal: *removeProp, - Summary: "Summary for the First consumer chain removal proposal", - Deposit: "1000uatom", - } - - consumerAddBody, err := json.MarshalIndent(addPropWithDeposit, "", " ") - s.Require().NoError(err) - - consumerRemoveBody, err := json.MarshalIndent(removePropWithDeposit, "", " ") - s.Require().NoError(err) - - err = writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalAddConsumerChainFilename), consumerAddBody) - s.Require().NoError(err) - err = writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalRemoveConsumerChainFilename), consumerRemoveBody) - s.Require().NoError(err) -} - -/* -AddRemoveConsumerChain tests adding and subsequently removing a new consumer chain to Gaia. -Test Benchmarks: -1. Submit and pass proposal to add consumer chain -2. Validation that consumer chain was added -3. Submit and pass proposal to remove consumer chain -4. Validation that consumer chain was removed -*/ -func (s *IntegrationTestSuite) AddRemoveConsumerChain() { - s.fundCommunityPool() - chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) - proposerAddress, _ := s.chainA.validators[0].keyInfo.GetAddress() - sender := proposerAddress.String() - consumerChainID := "consumer" - s.writeAddRemoveConsumerProposals(s.chainA, consumerChainID) - - // Gov tests may be run in arbitrary order, each test must increment proposalCounter to have the correct proposal id to submit and query - // Add Consumer Chain - proposalCounter++ - submitGovFlags := []string{"consumer-addition", configFile(proposalAddConsumerChainFilename)} - depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} - voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes"} - s.submitLegacyGovProposal(chainAAPIEndpoint, sender, proposalCounter, providertypes.ProposalTypeConsumerAddition, submitGovFlags, depositGovFlags, voteGovFlags, "vote", false) - - // Query and assert consumer has been added - s.execQueryConsumerChains(s.chainA, 0, gaiaHomePath, validateConsumerAddition, consumerChainID) - - // Remove Consumer Chain - proposalCounter++ - submitGovFlags = []string{"consumer-removal", configFile(proposalRemoveConsumerChainFilename)} - depositGovFlags = []string{strconv.Itoa(proposalCounter), depositAmount.String()} - voteGovFlags = []string{strconv.Itoa(proposalCounter), "yes"} - s.submitLegacyGovProposal(chainAAPIEndpoint, sender, proposalCounter, providertypes.ProposalTypeConsumerRemoval, submitGovFlags, depositGovFlags, voteGovFlags, "vote", false) - // Query and assert consumer has been removed - s.execQueryConsumerChains(s.chainA, 0, gaiaHomePath, validateConsumerRemoval, consumerChainID) -} - -func validateConsumerAddition(res providertypes.QueryConsumerChainsResponse, consumerChainID string) bool { - if res.Size() == 0 { - return false - } - for _, chain := range res.GetChains() { - return strings.Compare(chain.ChainId, consumerChainID) == 0 - } - return false -} - -func validateConsumerRemoval(res providertypes.QueryConsumerChainsResponse, consumerChainID string) bool { - if res.Size() > 0 { - for _, chain := range res.GetChains() { - if strings.Compare(chain.ChainId, consumerChainID) == 0 { - return false - } - } - } - return true -} diff --git a/tests/e2e/e2e_query_exec_test.go b/tests/e2e/e2e_query_exec_test.go deleted file mode 100644 index a8966fa9702..00000000000 --- a/tests/e2e/e2e_query_exec_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package e2e - -import ( - "context" - "fmt" - "time" - - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - - "github.com/cosmos/cosmos-sdk/client/flags" -) - -func (s *IntegrationTestSuite) execQueryConsumerChains( - c *chain, - valIdx int, - homePath string, - queryValidation func(res ccvtypes.QueryConsumerChainsResponse, consumerChainId string) bool, - consumerChainID string, -) { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - - s.T().Logf("Querying consumer chains for chain: %s", c.id) - gaiaCommand := []string{ - gaiadBinary, - "query", - "provider", - "list-consumer-chains", - fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), - fmt.Sprintf("--%s=%s", flags.FlagHome, homePath), - "--output=json", - } - - s.executeGaiaTxCommand(ctx, c, gaiaCommand, valIdx, s.validateQueryConsumers(queryValidation, consumerChainID)) - s.T().Logf("Successfully queried consumer chains for chain %s", c.id) -} - -func (s *IntegrationTestSuite) validateQueryConsumers(queryValidation func(ccvtypes.QueryConsumerChainsResponse, string) bool, consumerChainID string) func([]byte, []byte) bool { - return func(stdOut []byte, stdErr []byte) bool { - var queryConsumersRes ccvtypes.QueryConsumerChainsResponse - if err := cdc.UnmarshalJSON(stdOut, &queryConsumersRes); err != nil { - s.T().Logf("Error unmarshalling query consumer chains: %s", err.Error()) - return false - } - return queryValidation(queryConsumersRes, consumerChainID) - } -} diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index cff0449716f..dff8672cf1f 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -64,7 +64,6 @@ func (s *IntegrationTestSuite) TestGov() { s.GovCancelSoftwareUpgrade() s.GovCommunityPoolSpend() - s.AddRemoveConsumerChain() s.testSetBlocksPerEpoch() s.ExpeditedProposalRejected()