From 2fb0e6d5680f7b48917f2a7507c5ab6443844097 Mon Sep 17 00:00:00 2001 From: violet Date: Wed, 11 Dec 2024 10:29:28 -0500 Subject: [PATCH] fix: export only active consensus validators to genesis When exporting a chain to a genesis file, there's a field in the genesis called "validators" that contains the validators that'll be used to bootstrap consensus. This is separate to the list of all validators, bonded and otherwise, kept in the staking module's app_state. If this field contains anything other than those validators that're actively participating in consensus, the chain import will fail. Thus, we shouldn't add all the validators from the staking module to this field, but only those we want used for consensus. --- .../3445-export-consensus-validators.md | 2 + app/export.go | 13 +++ tests/interchain/integrator/export_test.go | 100 ++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 .changelog/unreleased/bug-fixes/3445-export-consensus-validators.md create mode 100644 tests/interchain/integrator/export_test.go diff --git a/.changelog/unreleased/bug-fixes/3445-export-consensus-validators.md b/.changelog/unreleased/bug-fixes/3445-export-consensus-validators.md new file mode 100644 index 00000000000..daf910e3473 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/3445-export-consensus-validators.md @@ -0,0 +1,2 @@ +- Export only validators that are participating in consensus + ([\#3445](https://github.com/cosmos/gaia/pull/3445)) diff --git a/app/export.go b/app/export.go index b82996ea886..f9d9236f1c6 100644 --- a/app/export.go +++ b/app/export.go @@ -2,6 +2,7 @@ package gaia import ( "encoding/json" + "sort" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" @@ -43,6 +44,18 @@ func (app *GaiaApp) ExportAppStateAndValidators( } validators, err := staking.WriteValidators(ctx, app.StakingKeeper) + if err != nil { + return servertypes.ExportedApp{}, err + } + sort.SliceStable(validators, func(i, j int) bool { + return validators[i].Power > validators[j].Power + }) + // we have to trim this to only active consensus validators + maxVals := app.ProviderKeeper.GetMaxProviderConsensusValidators(ctx) + if len(validators) > int(maxVals) { + validators = validators[:maxVals] + } + return servertypes.ExportedApp{ AppState: appState, Validators: validators, diff --git a/tests/interchain/integrator/export_test.go b/tests/interchain/integrator/export_test.go new file mode 100644 index 00000000000..7c32efc4704 --- /dev/null +++ b/tests/interchain/integrator/export_test.go @@ -0,0 +1,100 @@ +package integrator_test + +import ( + "fmt" + "testing" + + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/gaia/v22/tests/interchain/chainsuite" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/stretchr/testify/suite" +) + +const ( + maxValidators = 5 + maxConsensusValidators = 4 +) + +type ExportSuite struct { + *chainsuite.Suite +} + +func (s *ExportSuite) TestExportAndImportValidators() { + height, err := s.Chain.Height(s.GetContext()) + s.Require().NoError(err) + + s.Require().NoError(s.Chain.StopAllNodes(s.GetContext())) + + exported, err := s.Chain.ExportState(s.GetContext(), height) + s.Require().NoError(err) + newConfig := chainsuite.DefaultChainSpec(s.Env).ChainConfig + newConfig.ModifyGenesis = func(cc ibc.ChainConfig, b []byte) ([]byte, error) { + return []byte(exported), nil + } + newConfig.PreGenesis = func(c ibc.Chain) error { + for i, val := range s.Chain.Validators { + key, err := val.PrivValFileContent(s.GetContext()) + if err != nil { + return fmt.Errorf("failed to get key for validator %d: %w", i, err) + } + cosmosChain := c.(*cosmos.CosmosChain) + err = cosmosChain.Validators[i].OverwritePrivValFile(s.GetContext(), key) + if err != nil { + return fmt.Errorf("failed to overwrite priv val file for validator %d: %w", i, err) + } + } + return nil + } + zero := 0 + newSpec := &interchaintest.ChainSpec{ + Name: "gaia", + Version: s.Env.NewGaiaImageVersion, + NumValidators: &chainsuite.SixValidators, + NumFullNodes: &zero, + ChainConfig: newConfig, + } + newChain, err := chainsuite.CreateChain(s.GetContext(), s.T(), newSpec) + s.Require().NoError(err) + + validators := []*stakingtypes.Validator{} + for _, wallet := range s.Chain.ValidatorWallets { + validator, err := newChain.StakingQueryValidator(s.GetContext(), wallet.ValoperAddress) + s.Require().NoError(err) + validators = append(validators, validator) + } + for i := 0; i < maxValidators; i++ { + s.Require().Equal(stakingtypes.Bonded, validators[i].Status) + } + s.Require().Equal(stakingtypes.Unbonded, validators[maxValidators].Status) + + vals, err := newChain.QueryJSON(s.GetContext(), "validators", "tendermint-validator-set") + s.Require().NoError(err) + s.Require().Equal(maxConsensusValidators, len(vals.Array()), vals) + for i := 0; i < maxConsensusValidators; i++ { + valCons := vals.Array()[i].Get("address").String() + s.Require().NoError(err) + s.Require().Equal(newChain.ValidatorWallets[i].ValConsAddress, valCons) + } +} + +func TestExport(t *testing.T) { + genesis := chainsuite.DefaultGenesis() + genesis = append(genesis, + cosmos.NewGenesisKV("app_state.staking.params.max_validators", maxValidators), + cosmos.NewGenesisKV("app_state.provider.params.max_provider_consensus_validators", maxConsensusValidators), + ) + s := &ExportSuite{ + Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + UpgradeOnSetup: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + ChainConfig: ibc.ChainConfig{ + ModifyGenesis: cosmos.ModifyGenesis(genesis), + }, + }, + }), + } + suite.Run(t, s) +}