From 8111e4bcc1ec0104a86a2bba7a329b28ea69e345 Mon Sep 17 00:00:00 2001 From: violet Date: Tue, 19 Mar 2024 10:36:44 -0400 Subject: [PATCH 1/2] feat validators can have different genesis and self-delegation amounts this is done by making ModifyGenesisAmounts take in an index parameter; the returned values will be used for the validator at that index. --- chain/cosmos/cosmos_chain.go | 32 +++++++++++++++----------------- ibc/types.go | 4 ++-- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/chain/cosmos/cosmos_chain.go b/chain/cosmos/cosmos_chain.go index df3f209d8..410d0e4dc 100644 --- a/chain/cosmos/cosmos_chain.go +++ b/chain/cosmos/cosmos_chain.go @@ -872,28 +872,26 @@ func (c *CosmosChain) Start(testName string, ctx context.Context, additionalGene decimalPow := int64(math.Pow10(int(*chainCfg.CoinDecimals))) - genesisAmount := types.Coin{ - Amount: sdkmath.NewInt(10_000_000).MulRaw(decimalPow), - Denom: chainCfg.Denom, - } - - genesisSelfDelegation := types.Coin{ - Amount: sdkmath.NewInt(5_000_000).MulRaw(decimalPow), - Denom: chainCfg.Denom, - } - - if chainCfg.ModifyGenesisAmounts != nil { - genesisAmount, genesisSelfDelegation = chainCfg.ModifyGenesisAmounts() + genesisAmounts := make([][]types.Coin, len(c.Validators)) + genesisSelfDelegation := make([]types.Coin, len(c.Validators)) + + for i := range c.Validators { + genesisAmounts[i] = []types.Coin{{Amount: sdkmath.NewInt(10_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom}} + genesisSelfDelegation[i] = types.Coin{Amount: sdkmath.NewInt(5_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom} + if chainCfg.ModifyGenesisAmounts != nil { + amount, selfDelegation := chainCfg.ModifyGenesisAmounts(i) + genesisAmounts[i] = []types.Coin{amount} + genesisSelfDelegation[i] = selfDelegation + } } - genesisAmounts := []types.Coin{genesisAmount} - configFileOverrides := chainCfg.ConfigFileOverrides eg := new(errgroup.Group) // Initialize config and sign gentx for each validator. - for _, v := range c.Validators { + for i, v := range c.Validators { v := v + i := i v.Validator = true eg.Go(func() error { if err := v.InitFullNodeFiles(ctx); err != nil { @@ -917,7 +915,7 @@ func (c *CosmosChain) Start(testName string, ctx context.Context, additionalGene } } if !c.cfg.SkipGenTx { - return v.InitValidatorGenTx(ctx, &chainCfg, genesisAmounts, genesisSelfDelegation) + return v.InitValidatorGenTx(ctx, &chainCfg, genesisAmounts[i], genesisSelfDelegation[i]) } return nil }) @@ -975,7 +973,7 @@ func (c *CosmosChain) Start(testName string, ctx context.Context, additionalGene return err } - if err := validator0.AddGenesisAccount(ctx, bech32, genesisAmounts); err != nil { + if err := validator0.AddGenesisAccount(ctx, bech32, genesisAmounts[0]); err != nil { return err } diff --git a/ibc/types.go b/ibc/types.go index 8623647ec..261a39e27 100644 --- a/ibc/types.go +++ b/ibc/types.go @@ -42,8 +42,8 @@ type ChainConfig struct { PreGenesis func(ChainConfig) error // When provided, genesis file contents will be altered before sharing for genesis. ModifyGenesis func(ChainConfig, []byte) ([]byte, error) - // Modify genesis-amounts - ModifyGenesisAmounts func() (sdk.Coin, sdk.Coin) + // Modify genesis-amounts for the validator at the given index + ModifyGenesisAmounts func(int) (sdk.Coin, sdk.Coin) // Override config parameters for files at filepath. ConfigFileOverrides map[string]any // Non-nil will override the encoding config, used for cosmos chains only. From 6dd5e90b43cbe9fb37e4c6a4f232be324488e130 Mon Sep 17 00:00:00 2001 From: violet Date: Thu, 28 Mar 2024 16:43:24 -0400 Subject: [PATCH 2/2] Add a test for stake distribution in cosmos chains --- examples/cosmos/chain_genesis_stake_test.go | 98 +++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 examples/cosmos/chain_genesis_stake_test.go diff --git a/examples/cosmos/chain_genesis_stake_test.go b/examples/cosmos/chain_genesis_stake_test.go new file mode 100644 index 000000000..450c0f8df --- /dev/null +++ b/examples/cosmos/chain_genesis_stake_test.go @@ -0,0 +1,98 @@ +package cosmos_test + +import ( + "context" + "encoding/json" + "strconv" + "testing" + + types "github.com/cosmos/cosmos-sdk/types" + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testreporter" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +func TestChainGenesisUnequalStake(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + t.Parallel() + const ( + denom = "uatom" + val1_stake = 1_000_000_000 + val2_stake = 2_000_000_000 + balance = 1_000_000_000_000 + ) + validators := 2 + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + { + Name: "gaia", + ChainName: "gaia", + Version: "v15.1.0", + NumValidators: &validators, + ChainConfig: ibc.ChainConfig{ + Denom: denom, + ModifyGenesisAmounts: func(i int) (types.Coin, types.Coin) { + if i == 0 { + return types.NewCoin(denom, types.NewInt(balance)), types.NewCoin(denom, types.NewInt(val1_stake)) + } + return types.NewCoin(denom, types.NewInt(balance)), types.NewCoin(denom, types.NewInt(val2_stake)) + }, + }, + }, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + client, network := interchaintest.DockerSetup(t) + + chain := chains[0].(*cosmos.CosmosChain) + + ic := interchaintest.NewInterchain(). + AddChain(chain) + rep := testreporter.NewNopReporter() + + err = ic.Build(context.Background(), rep.RelayerExecReporter(t), interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), + SkipPathCreation: false, + }) + require.NoError(t, err) + t.Cleanup(func() { + _ = ic.Close() + }) + + stdout, _, err := chain.GetNode().ExecQuery(context.Background(), "staking", "validators") + require.NoError(t, err) + + var validatorsResp map[string]interface{} + require.NoError(t, json.Unmarshal(stdout, &validatorsResp)) + require.Contains(t, validatorsResp, "validators") + validatorsList := validatorsResp["validators"].([]interface{}) + require.Len(t, validatorsList, 2) + + tokens1 := validatorsList[0].(map[string]interface{})["tokens"].(string) + tokens2 := validatorsList[1].(map[string]interface{})["tokens"].(string) + require.NotEmpty(t, tokens1) + require.NotEmpty(t, tokens2) + + tokens1Int, err := strconv.Atoi(tokens1) + require.NoError(t, err) + tokens2Int, err := strconv.Atoi(tokens2) + require.NoError(t, err) + + if tokens1Int > tokens2Int { + require.Equal(t, val2_stake, tokens1Int) + require.Equal(t, val1_stake, tokens2Int) + } else { + require.Equal(t, val1_stake, tokens1Int) + require.Equal(t, val2_stake, tokens2Int) + } + +}