diff --git a/.github/workflows/interchain-test.yml b/.github/workflows/interchain-test.yml index 38b16906b1c..8de73e3264e 100644 --- a/.github/workflows/interchain-test.yml +++ b/.github/workflows/interchain-test.yml @@ -42,7 +42,7 @@ jobs: matrix: ${{fromJson(needs.prepare-matrix.outputs.matrix)}} fail-fast: false - max-parallel: 3 + max-parallel: 10 steps: - name: Check out repository code uses: actions/checkout@v4 diff --git a/tests/interchain/chainsuite/chain_ics.go b/tests/interchain/chainsuite/chain_ics.go index 03f0652ffa0..0036b276489 100644 --- a/tests/interchain/chainsuite/chain_ics.go +++ b/tests/interchain/chainsuite/chain_ics.go @@ -31,7 +31,7 @@ type ConsumerConfig struct { ChainName string Version string Denom string - ShouldCopyProviderKey [ValidatorCount]bool + ShouldCopyProviderKey []bool TopN int ValidatorSetCap int ValidatorPowerCap int @@ -100,8 +100,8 @@ func newProposalWaiter() *proposalWaiter { func (p *Chain) AddConsumerChain(ctx context.Context, relayer *Relayer, config ConsumerConfig) (*Chain, error) { dockerClient, dockerNetwork := GetDockerContext(ctx) - if len(config.ShouldCopyProviderKey) != ValidatorCount { - return nil, fmt.Errorf("shouldCopyProviderKey must be the same length as the number of validators") + if len(config.ShouldCopyProviderKey) < len(p.Validators) { + return nil, fmt.Errorf("shouldCopyProviderKey should have at least %d elements", len(p.Validators)) } spawnTime := time.Now().Add(ChainSpawnWait) diff --git a/tests/interchain/chainsuite/config.go b/tests/interchain/chainsuite/config.go index 0ff1ef2f222..eed5db7c884 100644 --- a/tests/interchain/chainsuite/config.go +++ b/tests/interchain/chainsuite/config.go @@ -43,7 +43,7 @@ const ( DowntimeJailDuration = 10 * time.Second ProviderSlashingWindow = 10 GasPrices = "0.005" + Uatom - ValidatorCount = 6 + // ValidatorCount = 1 UpgradeDelta = 30 ValidatorFunds = 11_000_000_000 ChainSpawnWait = 155 * time.Second @@ -57,6 +57,12 @@ const ( ICSUidGuid = "1025:1025" ) +// These have to be vars so we can take their address +var ( + OneValidator int = 1 + SixValidators int = 6 +) + func MergeChainSpecs(spec, other *interchaintest.ChainSpec) *interchaintest.ChainSpec { if spec == nil { return other @@ -96,12 +102,15 @@ func (c SuiteConfig) Merge(other SuiteConfig) SuiteConfig { func DefaultGenesisAmounts(denom string) func(i int) (types.Coin, types.Coin) { return func(i int) (types.Coin, types.Coin) { + if i >= SixValidators { + panic("your chain has too many validators") + } return types.Coin{ Denom: denom, Amount: sdkmath.NewInt(ValidatorFunds), }, types.Coin{ Denom: denom, - Amount: sdkmath.NewInt([ValidatorCount]int64{ + Amount: sdkmath.NewInt([]int64{ 30_000_000, 29_000_000, 20_000_000, @@ -115,7 +124,6 @@ func DefaultGenesisAmounts(denom string) func(i int) (types.Coin, types.Coin) { func DefaultChainSpec(env Environment) *interchaintest.ChainSpec { fullNodes := 0 - validators := ValidatorCount var repository string if env.DockerRegistry == "" { repository = env.GaiaImageName @@ -125,7 +133,7 @@ func DefaultChainSpec(env Environment) *interchaintest.ChainSpec { return &interchaintest.ChainSpec{ Name: "gaia", NumFullNodes: &fullNodes, - NumValidators: &validators, + NumValidators: &OneValidator, Version: env.OldGaiaImageVersion, ChainConfig: ibc.ChainConfig{ Denom: Uatom, diff --git a/tests/interchain/consumer_chain/changeover_test.go b/tests/interchain/consumer_chain/changeover_test.go new file mode 100644 index 00000000000..7f88fd23959 --- /dev/null +++ b/tests/interchain/consumer_chain/changeover_test.go @@ -0,0 +1,242 @@ +package consumer_chain_test + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "github.com/stretchr/testify/suite" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" + "golang.org/x/sync/errgroup" +) + +type ChangeoverSuite struct { + *chainsuite.Suite + consumerCfg chainsuite.ConsumerConfig + Consumer *chainsuite.Chain +} + +func (s *ChangeoverSuite) SetupSuite() { + s.Suite.SetupSuite() + validators := 1 + fullNodes := 0 + genesisChanges := []cosmos.GenesisKV{ + cosmos.NewGenesisKV("app_state.gov.params.voting_period", chainsuite.GovVotingPeriod.String()), + cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", chainsuite.GovDepositPeriod.String()), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.denom", chainsuite.Ucon), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.amount", strconv.Itoa(chainsuite.GovMinDepositAmount)), + } + spec := &interchaintest.ChainSpec{ + Name: "ics-consumer", + ChainName: "ics-consumer", + // Unfortunately, this rc is a bit of a bespoke version; it corresponds to an rc + // in hypha's fork that has a fix for sovereign -> consumer changeovers + Version: "v6.2.0-rc1", + NumValidators: &validators, + NumFullNodes: &fullNodes, + ChainConfig: ibc.ChainConfig{ + Denom: chainsuite.Ucon, + GasPrices: "0.025" + chainsuite.Ucon, + GasAdjustment: 2.0, + Gas: "auto", + ConfigFileOverrides: map[string]any{ + "config/config.toml": chainsuite.DefaultConfigToml(), + }, + ModifyGenesisAmounts: chainsuite.DefaultGenesisAmounts(chainsuite.Ucon), + ModifyGenesis: cosmos.ModifyGenesis(genesisChanges), + Bin: "interchain-security-sd", + Images: []ibc.DockerImage{ + { + Repository: chainsuite.HyphaICSRepo, + Version: "v6.2.0-rc1", + UidGid: chainsuite.ICSUidGuid, + }, + }, + Bech32Prefix: "consumer", + }, + } + consumer, err := s.Chain.AddLinkedChain(s.GetContext(), s.T(), s.Relayer, spec) + s.Require().NoError(err) + + s.Consumer = consumer + + s.UpgradeChain() +} + +func (s *ChangeoverSuite) TestRewardsWithChangeover() { + transferCh, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, s.Consumer) + s.Require().NoError(err) + rewardDenom := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", transferCh.ChannelID, s.Consumer.Config().Denom)).IBCDenom() + + s.Run("changeover", func() { + s.changeSovereignToConsumer(s.Consumer, transferCh) + }) + + s.Run("rewards", func() { + govAuthority, err := s.Chain.GetGovernanceAddress(s.GetContext()) + s.Require().NoError(err) + rewardDenomsProp := providertypes.MsgChangeRewardDenoms{ + DenomsToAdd: []string{rewardDenom}, + Authority: govAuthority, + } + prop, err := s.Chain.BuildProposal([]cosmos.ProtoMessage{&rewardDenomsProp}, + "add denoms to list of registered reward denoms", + "add denoms to list of registered reward denoms", + "", chainsuite.GovDepositAmount, "", false) + s.Require().NoError(err) + propResult, err := s.Chain.SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop) + s.Require().NoError(err) + s.Require().NoError(s.Chain.PassProposal(s.GetContext(), propResult.ProposalID)) + + faucetAddrBts, err := s.Consumer.GetAddress(s.GetContext(), interchaintest.FaucetAccountKeyName) + s.Require().NoError(err) + faucetAddr := types.MustBech32ifyAddressBytes(s.Consumer.Config().Bech32Prefix, faucetAddrBts) + _, err = s.Consumer.Validators[0].ExecTx( + s.GetContext(), interchaintest.FaucetAccountKeyName, + "bank", "send", string(faucetAddr), s.Consumer.ValidatorWallets[0].Address, + "1"+s.Consumer.Config().Denom, "--fees", "100000000"+s.Consumer.Config().Denom, + ) + s.Require().NoError(err) + + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), chainsuite.BlocksPerDistribution+2, s.Chain, s.Consumer)) + s.Require().NoError(s.Relayer.ClearTransferChannel(s.GetContext(), s.Chain, s.Consumer)) + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain, s.Consumer)) + + rewardStr, err := s.Chain.QueryJSON( + s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", rewardDenom), + "distribution", "rewards", s.Chain.ValidatorWallets[0].Address, + ) + s.Require().NoError(err) + rewards, err := chainsuite.StrToSDKInt(rewardStr.String()) + s.Require().NoError(err) + s.Require().True(rewards.GT(sdkmath.NewInt(0)), "rewards: %s", rewards.String()) + }) +} + +func TestChangeover(t *testing.T) { + s := &ChangeoverSuite{ + Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + CreateRelayer: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.OneValidator, + }, + }), + consumerCfg: chainsuite.ConsumerConfig{ + ChainName: "ics-consumer", + ShouldCopyProviderKey: allProviderKeysCopied(), + Denom: chainsuite.Ucon, + TopN: 0, + AllowInactiveVals: true, + MinStake: 1_000_000, + }, + } + suite.Run(t, s) +} + +func (s *ChangeoverSuite) changeSovereignToConsumer(consumer *chainsuite.Chain, transferCh *ibc.ChannelOutput) { + cfg := s.consumerCfg + currentHeight, err := consumer.Height(s.GetContext()) + s.Require().NoError(err) + initialHeight := uint64(currentHeight) + 60 + cfg.InitialHeight = initialHeight + spawnTime := time.Now().Add(60 * time.Second) + cfg.DistributionTransmissionChannel = transferCh.ChannelID + + err = s.Chain.CreateConsumerPermissionless(s.GetContext(), consumer.Config().ChainID, cfg, spawnTime) + s.Require().NoError(err) + + consumerChains, _, err := s.Chain.GetNode().ExecQuery(s.GetContext(), "provider", "list-consumer-chains") + s.Require().NoError(err) + consumerChain := gjson.GetBytes(consumerChains, fmt.Sprintf("chains.#(chain_id=%q)", consumer.Config().ChainID)) + consumerID := consumerChain.Get("consumer_id").String() + + eg := errgroup.Group{} + for i := range consumer.Validators { + i := i + eg.Go(func() error { + key, _, err := consumer.Validators[i].ExecBin(s.GetContext(), "tendermint", "show-validator") + if err != nil { + return err + } + keyStr := strings.TrimSpace(string(key)) + _, err = s.Chain.Validators[i].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[i].Moniker, "provider", "opt-in", consumerID, keyStr) + return err + }) + } + s.Require().NoError(eg.Wait()) + + s.Require().NoError(err) + time.Sleep(time.Until(spawnTime)) + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain)) + + proposal := cosmos.SoftwareUpgradeProposal{ + Deposit: "5000000" + chainsuite.Ucon, + Title: "Changeover", + Name: "sovereign-changeover", + Description: "Changeover", + Height: int64(initialHeight) - 3, + } + upgradeTx, err := consumer.UpgradeProposal(s.GetContext(), interchaintest.FaucetAccountKeyName, proposal) + s.Require().NoError(err) + err = consumer.PassProposal(s.GetContext(), upgradeTx.ProposalID) + s.Require().NoError(err) + + currentHeight, err = consumer.Height(s.GetContext()) + s.Require().NoError(err) + + timeoutCtx, timeoutCtxCancel := context.WithTimeout(s.GetContext(), (time.Duration(int64(initialHeight)-currentHeight)+10)*chainsuite.CommitTimeout) + defer timeoutCtxCancel() + err = testutil.WaitForBlocks(timeoutCtx, int(int64(initialHeight)-currentHeight)+3, consumer) + s.Require().Error(err) + + s.Require().NoError(consumer.StopAllNodes(s.GetContext())) + + genesis, err := consumer.GetNode().GenesisFileContent(s.GetContext()) + s.Require().NoError(err) + + ccvState, _, err := s.Chain.GetNode().ExecQuery(s.GetContext(), "provider", "consumer-genesis", consumerID) + s.Require().NoError(err) + genesis, err = sjson.SetRawBytes(genesis, "app_state.ccvconsumer", ccvState) + s.Require().NoError(err) + + genesis, err = sjson.SetBytes(genesis, "app_state.slashing.params.signed_blocks_window", strconv.Itoa(chainsuite.SlashingWindowConsumer)) + s.Require().NoError(err) + genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.params.reward_denoms", []string{chainsuite.Ucon}) + s.Require().NoError(err) + genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.params.provider_reward_denoms", []string{s.Chain.Config().Denom}) + s.Require().NoError(err) + genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.params.blocks_per_distribution_transmission", chainsuite.BlocksPerDistribution) + s.Require().NoError(err) + + for _, val := range consumer.Validators { + val := val + eg.Go(func() error { + if err := val.OverwriteGenesisFile(s.GetContext(), []byte(genesis)); err != nil { + return err + } + return val.WriteFile(s.GetContext(), []byte(genesis), ".sovereign/config/genesis.json") + }) + } + s.Require().NoError(eg.Wait()) + + consumer.ChangeBinary(s.GetContext(), "interchain-security-cdd") + s.Require().NoError(consumer.StartAllNodes(s.GetContext())) + s.Require().NoError(s.Relayer.ConnectProviderConsumer(s.GetContext(), s.Chain, consumer)) + s.Require().NoError(s.Relayer.StopRelayer(s.GetContext(), chainsuite.GetRelayerExecReporter(s.GetContext()))) + s.Require().NoError(s.Relayer.StartRelayer(s.GetContext(), chainsuite.GetRelayerExecReporter(s.GetContext()))) + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) +} diff --git a/tests/interchain/consumer_launch_test.go b/tests/interchain/consumer_chain/consumer_launch_test.go similarity index 59% rename from tests/interchain/consumer_launch_test.go rename to tests/interchain/consumer_chain/consumer_launch_test.go index ae4c343adc8..7ea2635300f 100644 --- a/tests/interchain/consumer_launch_test.go +++ b/tests/interchain/consumer_chain/consumer_launch_test.go @@ -1,4 +1,4 @@ -package interchain_test +package consumer_chain_test import ( "testing" @@ -16,19 +16,19 @@ type ConsumerLaunchSuite struct { OtherChain string OtherChainVersionPreUpgrade string OtherChainVersionPostUpgrade string - ShouldCopyProviderKey [chainsuite.ValidatorCount]bool + ShouldCopyProviderKey []bool } -func noProviderKeysCopied() [chainsuite.ValidatorCount]bool { - return [chainsuite.ValidatorCount]bool{false, false, false, false, false, false} +func noProviderKeysCopied() []bool { + return []bool{false, false, false, false, false, false} } -func allProviderKeysCopied() [chainsuite.ValidatorCount]bool { - return [chainsuite.ValidatorCount]bool{true, true, true, true, true, true} +func allProviderKeysCopied() []bool { + return []bool{true, true, true, true, true, true} } -func someProviderKeysCopied() [chainsuite.ValidatorCount]bool { - return [chainsuite.ValidatorCount]bool{true, false, true, false, true, false} +func someProviderKeysCopied() []bool { + return []bool{true, false, true, false, true, false} } func (s *ConsumerLaunchSuite) TestChainLaunch() { @@ -93,7 +93,12 @@ func selectConsumerVersion(preV21, postV21 string) string { func TestICS4ChainLaunch(t *testing.T) { s := &ConsumerLaunchSuite{ - Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{CreateRelayer: true}), + Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + CreateRelayer: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + }, + }), OtherChain: "ics-consumer", OtherChainVersionPreUpgrade: selectConsumerVersion("v4.4.1", "v4.5.0"), OtherChainVersionPostUpgrade: "v4.5.0", @@ -104,7 +109,12 @@ func TestICS4ChainLaunch(t *testing.T) { func TestICS6ConsumerAllKeysChainLaunch(t *testing.T) { s := &ConsumerLaunchSuite{ - Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{CreateRelayer: true}), + Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + CreateRelayer: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + }, + }), OtherChain: "ics-consumer", OtherChainVersionPreUpgrade: selectConsumerVersion("v6.0.0", "v6.2.1"), OtherChainVersionPostUpgrade: "v6.2.1", @@ -115,7 +125,12 @@ func TestICS6ConsumerAllKeysChainLaunch(t *testing.T) { func TestICS6ConsumerSomeKeysChainLaunch(t *testing.T) { s := &ConsumerLaunchSuite{ - Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{CreateRelayer: true}), + Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + CreateRelayer: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + }, + }), OtherChain: "ics-consumer", OtherChainVersionPreUpgrade: selectConsumerVersion("v6.0.0", "v6.2.1"), OtherChainVersionPostUpgrade: "v6.2.1", @@ -126,7 +141,12 @@ func TestICS6ConsumerSomeKeysChainLaunch(t *testing.T) { func TestICS6ConsumerNoKeysChainLaunch(t *testing.T) { s := &ConsumerLaunchSuite{ - Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{CreateRelayer: true}), + Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + CreateRelayer: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + }, + }), OtherChain: "ics-consumer", OtherChainVersionPreUpgrade: selectConsumerVersion("v6.0.0", "v6.2.1"), OtherChainVersionPostUpgrade: "v6.2.1", @@ -134,47 +154,3 @@ func TestICS6ConsumerNoKeysChainLaunch(t *testing.T) { } suite.Run(t, s) } - -type MainnetConsumerChainsSuite struct { - *chainsuite.Suite -} - -func (s *MainnetConsumerChainsSuite) TestMainnetConsumerChainsAfterUpgrade() { - // We can't do these consumer launches yet because the chains aren't compatible with launching on v21 yet - if semver.Major(s.Env.OldGaiaImageVersion) == s.Env.UpgradeName && s.Env.UpgradeName == "v21" { - s.T().Skip("Skipping Consumer Launch tests when going from v21 -> v21") - } - neutron, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, chainsuite.ConsumerConfig{ - ChainName: "neutron", - Version: chainsuite.NeutronVersion, - ShouldCopyProviderKey: allProviderKeysCopied(), - Denom: chainsuite.NeutronDenom, - TopN: 95, - }) - s.Require().NoError(err) - stride, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, chainsuite.ConsumerConfig{ - ChainName: "stride", - Version: chainsuite.StrideVersion, - ShouldCopyProviderKey: allProviderKeysCopied(), - Denom: chainsuite.StrideDenom, - TopN: 95, - }) - s.Require().NoError(err) - - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), neutron, s.Relayer, 1_000_000, 0, 1)) - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), stride, s.Relayer, 1_000_000, 0, 1)) - - s.UpgradeChain() - - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), neutron, s.Relayer, 1_000_000, 0, 1)) - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), stride, s.Relayer, 1_000_000, 0, 1)) - s.Require().NoError(chainsuite.SendSimpleIBCTx(s.GetContext(), s.Chain, neutron, s.Relayer)) - s.Require().NoError(chainsuite.SendSimpleIBCTx(s.GetContext(), s.Chain, stride, s.Relayer)) -} - -func TestMainnetConsumerChainsAfterUpgrade(t *testing.T) { - s := &MainnetConsumerChainsSuite{ - Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{CreateRelayer: true}), - } - suite.Run(t, s) -} diff --git a/tests/interchain/consumer_chain/consumer_modification_test.go b/tests/interchain/consumer_chain/consumer_modification_test.go new file mode 100644 index 00000000000..d4239efa6ca --- /dev/null +++ b/tests/interchain/consumer_chain/consumer_modification_test.go @@ -0,0 +1,409 @@ +package consumer_chain_test + +import ( + "context" + "encoding/json" + "fmt" + "path" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "github.com/stretchr/testify/suite" + "golang.org/x/sync/errgroup" +) + +const ( + permissionlessDepositPeriod = 7 * time.Minute +) + +type ConsumerModificationSuite struct { + *chainsuite.Suite + consumerCfg chainsuite.ConsumerConfig +} + +func (s *ConsumerModificationSuite) TestChangeOwner() { + cfg := s.consumerCfg + cfg.TopN = 0 + cfg.BeforeSpawnTime = func(ctx context.Context, consumer *cosmos.CosmosChain) { + consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) + s.Require().NoError(err) + eg := errgroup.Group{} + for i := 0; i < 3; i++ { + i := i + eg.Go(func() error { + _, err := s.Chain.Validators[i].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[i].Moniker, "provider", "opt-in", consumerID) + return err + }) + } + s.Require().NoError(eg.Wait()) + } + consumer, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, cfg) + s.Require().NoError(err) + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) + + govAddress, err := s.Chain.GetGovernanceAddress(s.GetContext()) + s.Require().NoError(err) + consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) + s.Require().NoError(err) + update := &providertypes.MsgUpdateConsumer{ + ConsumerId: consumerID, + NewOwnerAddress: govAddress, + Metadata: &providertypes.ConsumerMetadata{ + Name: consumer.Config().Name, + Description: "Consumer chain", + Metadata: "ipfs://", + }, + } + updateBz, err := json.Marshal(update) + s.Require().NoError(err) + err = s.Chain.GetNode().WriteFile(s.GetContext(), updateBz, "consumer-update.json") + s.Require().NoError(err) + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, + "provider", "update-consumer", path.Join(s.Chain.GetNode().HomeDir(), "consumer-update.json")) + s.Require().NoError(err) + + update.Owner = govAddress + update.NewOwnerAddress = s.Chain.ValidatorWallets[0].Address + prop, err := s.Chain.BuildProposal([]cosmos.ProtoMessage{update}, + "update consumer", "update consumer", "", + chainsuite.GovDepositAmount, "", false) + s.Require().NoError(err) + txhash, err := s.Chain.GetNode().SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop) + s.Require().NoError(err) + propID, err := s.Chain.GetProposalID(s.GetContext(), txhash) + s.Require().NoError(err) + s.Require().NoError(s.Chain.PassProposal(s.GetContext(), propID)) +} + +func (s *ConsumerModificationSuite) TestChangePowerShaping() { + cfg := s.consumerCfg + cfg.TopN = 0 + const ( + oldValidatorCount = 4 + newValidatorCount = 3 + ) + cfg.BeforeSpawnTime = func(ctx context.Context, consumer *cosmos.CosmosChain) { + consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) + s.Require().NoError(err) + eg := errgroup.Group{} + for i := 0; i < oldValidatorCount; i++ { + i := i + eg.Go(func() error { + _, err := s.Chain.Validators[i].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[i].Moniker, "provider", "opt-in", consumerID) + return err + }) + } + s.Require().NoError(eg.Wait()) + } + consumer, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, cfg) + s.Require().NoError(err) + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) + + consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) + s.Require().NoError(err) + update := &providertypes.MsgUpdateConsumer{ + ConsumerId: consumerID, + Metadata: &providertypes.ConsumerMetadata{ + Name: consumer.Config().Name, + Description: "Consumer chain", + Metadata: "ipfs://", + }, + PowerShapingParameters: &providertypes.PowerShapingParameters{ + ValidatorSetCap: newValidatorCount, + }, + } + updateBz, err := json.Marshal(update) + s.Require().NoError(err) + err = s.Chain.GetNode().WriteFile(s.GetContext(), updateBz, "consumer-update.json") + s.Require().NoError(err) + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, + "provider", "update-consumer", path.Join(s.Chain.GetNode().HomeDir(), "consumer-update.json")) + s.Require().NoError(err) + + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) + + vals, err := consumer.QueryJSON(s.GetContext(), "validators", "tendermint-validator-set") + s.Require().NoError(err) + s.Require().Equal(newValidatorCount, len(vals.Array()), vals) + for i := 0; i < newValidatorCount; i++ { + valCons := vals.Array()[i].Get("address").String() + s.Require().NoError(err) + s.Require().Equal(consumer.ValidatorWallets[i].ValConsAddress, valCons) + } +} + +func (s *ConsumerModificationSuite) TestConsumerCommissionRate() { + cfg := s.consumerCfg + cfg.TopN = 0 + cfg.BeforeSpawnTime = func(ctx context.Context, consumer *cosmos.CosmosChain) { + consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) + s.Require().NoError(err) + _, err = s.Chain.Validators[0].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "opt-in", consumerID) + s.Require().NoError(err) + } + + images := []ibc.DockerImage{ + { + Repository: "ghcr.io/hyphacoop/ics", + Version: "v4.5.0", + UidGid: "1025:1025", + }, + } + chainID := fmt.Sprintf("%s-test-%d", cfg.ChainName, len(s.Chain.Consumers)+1) + spawnTime := time.Now().Add(chainsuite.ChainSpawnWait) + cfg.Spec = s.Chain.DefaultConsumerChainSpec(s.GetContext(), chainID, cfg, spawnTime, nil) + cfg.Spec.Version = "v4.5.0" + cfg.Spec.Images = images + consumer1, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, cfg) + s.Require().NoError(err) + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer1, s.Relayer, 1_000_000, 0, 1)) + + chainID = fmt.Sprintf("%s-test-%d", cfg.ChainName, len(s.Chain.Consumers)+1) + spawnTime = time.Now().Add(chainsuite.ChainSpawnWait) + cfg.Spec = s.Chain.DefaultConsumerChainSpec(s.GetContext(), chainID, cfg, spawnTime, nil) + cfg.Spec.Version = "v4.5.0" + cfg.Spec.Images = images + consumer2, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, cfg) + s.Require().NoError(err) + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer2, s.Relayer, 1_000_000, 0, 1)) + + for i := 1; i < len(consumer1.Validators); i++ { + s.Require().NoError(consumer1.Validators[i].StopContainer(s.GetContext())) + s.Require().NoError(consumer2.Validators[i].StopContainer(s.GetContext())) + } + + consumer1Ch, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, consumer1) + s.Require().NoError(err) + consumer2Ch, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, consumer2) + s.Require().NoError(err) + denom1 := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", consumer1Ch.ChannelID, consumer1.Config().Denom)).IBCDenom() + denom2 := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", consumer2Ch.ChannelID, consumer2.Config().Denom)).IBCDenom() + + s.Require().NotEqual(denom1, denom2, "denom1: %s, denom2: %s; channel1: %s, channel2: %s", denom1, denom2, consumer1Ch.Counterparty.ChannelID, consumer2Ch.Counterparty.ChannelID) + + govAuthority, err := s.Chain.GetGovernanceAddress(s.GetContext()) + s.Require().NoError(err) + rewardDenomsProp := providertypes.MsgChangeRewardDenoms{ + DenomsToAdd: []string{denom1, denom2}, + Authority: govAuthority, + } + prop, err := s.Chain.BuildProposal([]cosmos.ProtoMessage{&rewardDenomsProp}, + "add denoms to list of registered reward denoms", + "add denoms to list of registered reward denoms", + "", chainsuite.GovDepositAmount, "", false) + s.Require().NoError(err) + propResult, err := s.Chain.SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop) + s.Require().NoError(err) + s.Require().NoError(s.Chain.PassProposal(s.GetContext(), propResult.ProposalID)) + + eg := errgroup.Group{} + + _, err = s.Chain.Validators[0].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-all-rewards") + s.Require().NoError(err) + + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, "--commission") + s.Require().NoError(err) + + consumerID1, err := s.Chain.GetConsumerID(s.GetContext(), consumer1.Config().ChainID) + s.Require().NoError(err) + consumerID2, err := s.Chain.GetConsumerID(s.GetContext(), consumer2.Config().ChainID) + s.Require().NoError(err) + + eg.Go(func() error { + _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "set-consumer-commission-rate", consumerID1, "0.5") + return err + }) + eg.Go(func() error { + _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "set-consumer-commission-rate", consumerID2, "0.5") + return err + }) + s.Require().NoError(eg.Wait()) + + _, err = s.Chain.Validators[0].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, "--commission") + s.Require().NoError(err) + + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 1, consumer1, consumer2)) + + eg.Go(func() error { + _, err := consumer1.Validators[0].ExecTx(s.GetContext(), consumer1.ValidatorWallets[0].Moniker, "bank", "send", consumer1.ValidatorWallets[0].Address, consumer1.ValidatorWallets[1].Address, "1"+consumer1.Config().Denom, "--fees", "100000000"+consumer1.Config().Denom) + return err + }) + eg.Go(func() error { + _, err := consumer2.Validators[0].ExecTx(s.GetContext(), consumer2.ValidatorWallets[0].Moniker, "bank", "send", consumer2.ValidatorWallets[0].Address, consumer2.ValidatorWallets[1].Address, "1"+consumer2.Config().Denom, "--fees", "100000000"+consumer2.Config().Denom) + return err + }) + s.Require().NoError(eg.Wait()) + + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), chainsuite.BlocksPerDistribution+2, s.Chain, consumer1, consumer2)) + s.Require().NoError(s.Relayer.ClearTransferChannel(s.GetContext(), s.Chain, consumer1)) + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain, consumer1, consumer2)) + + rewardStr, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", denom1), "distribution", "rewards", s.Chain.ValidatorWallets[0].Address) + s.Require().NoError(err) + rewardsDenom1, err := chainsuite.StrToSDKInt(rewardStr.String()) + s.Require().NoError(err) + rewardStr, err = s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", denom2), "distribution", "rewards", s.Chain.ValidatorWallets[0].Address) + s.Require().NoError(err) + rewardsDenom2, err := chainsuite.StrToSDKInt(rewardStr.String()) + s.Require().NoError(err) + + s.Require().NotEmpty(rewardsDenom1) + s.Require().NotEmpty(rewardsDenom2) + s.Require().True(rewardsDenom1.Sub(rewardsDenom2).Abs().LT(sdkmath.NewInt(1000)), "rewards1Int: %s, rewards2Int: %s", rewardsDenom1.String(), rewardsDenom2.String()) + + _, err = s.Chain.Validators[0].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, "--commission") + s.Require().NoError(err) + + eg.Go(func() error { + _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "set-consumer-commission-rate", consumerID1, "0.25") + return err + }) + eg.Go(func() error { + _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "set-consumer-commission-rate", consumerID2, "0.5") + return err + }) + s.Require().NoError(eg.Wait()) + + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, "--commission") + s.Require().NoError(err) + + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 1, consumer1, consumer2)) + + eg.Go(func() error { + _, err := consumer1.Validators[0].ExecTx(s.GetContext(), consumer1.ValidatorWallets[0].Moniker, "bank", "send", consumer1.ValidatorWallets[0].Address, consumer1.ValidatorWallets[1].Address, "1"+consumer1.Config().Denom, "--fees", "100000000"+consumer1.Config().Denom) + return err + }) + eg.Go(func() error { + _, err := consumer2.Validators[0].ExecTx(s.GetContext(), consumer2.ValidatorWallets[0].Moniker, "bank", "send", consumer2.ValidatorWallets[0].Address, consumer2.ValidatorWallets[1].Address, "1"+consumer2.Config().Denom, "--fees", "100000000"+consumer2.Config().Denom) + return err + }) + s.Require().NoError(eg.Wait()) + + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), chainsuite.BlocksPerDistribution+2, s.Chain, consumer1, consumer2)) + s.Require().NoError(s.Relayer.ClearTransferChannel(s.GetContext(), s.Chain, consumer1)) + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain, consumer1, consumer2)) + + rewardStr, err = s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", denom1), "distribution", "rewards", s.Chain.ValidatorWallets[0].Address) + s.Require().NoError(err) + rewardsDenom1, err = chainsuite.StrToSDKInt(rewardStr.String()) + s.Require().NoError(err) + rewardStr, err = s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", denom2), "distribution", "rewards", s.Chain.ValidatorWallets[0].Address) + s.Require().NoError(err) + rewardsDenom2, err = chainsuite.StrToSDKInt(rewardStr.String()) + s.Require().NoError(err) + + s.Require().True(rewardsDenom1.GT(rewardsDenom2), "rewards1Int: %s, rewards2Int: %s", rewardsDenom1.String(), rewardsDenom2.String()) + s.Require().False(rewardsDenom1.Sub(rewardsDenom2).Abs().LT(sdkmath.NewInt(1000)), "rewards1Int: %s, rewards2Int: %s", rewardsDenom1.String(), rewardsDenom2.String()) +} + +func (s *ConsumerModificationSuite) TestLaunchWithAllowListThenModify() { + consumerConfig := s.consumerCfg + consumerConfig.Allowlist = []string{ + s.Chain.ValidatorWallets[0].ValConsAddress, + s.Chain.ValidatorWallets[1].ValConsAddress, + s.Chain.ValidatorWallets[2].ValConsAddress, + } + consumerConfig.TopN = 0 + consumerConfig.BeforeSpawnTime = func(ctx context.Context, consumer *cosmos.CosmosChain) { + consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) + s.Require().NoError(err) + eg := errgroup.Group{} + for i := 0; i < 3; i++ { + i := i + eg.Go(func() error { + _, err := s.Chain.Validators[i].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[i].Moniker, "provider", "opt-in", consumerID) + return err + }) + } + s.Require().NoError(eg.Wait()) + } + + consumer, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, consumerConfig) + s.Require().NoError(err) + + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) + + consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) + s.Require().NoError(err) + + // ensure we can't opt in a non-allowlisted validator + _, err = s.Chain.Validators[3].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[3].Moniker, + "provider", "opt-in", consumerID) + s.Require().NoError(err) + + validators, err := consumer.QueryJSON(s.GetContext(), "validators", "tendermint-validator-set") + s.Require().NoError(err) + s.Require().Equal(3, len(validators.Array())) + + update := &providertypes.MsgUpdateConsumer{ + ConsumerId: consumerID, + PowerShapingParameters: &providertypes.PowerShapingParameters{ + Allowlist: []string{}, + }, + } + updateBz, err := json.Marshal(update) + s.Require().NoError(err) + err = s.Chain.GetNode().WriteFile(s.GetContext(), updateBz, "consumer-update.json") + s.Require().NoError(err) + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, + "provider", "update-consumer", path.Join(s.Chain.GetNode().HomeDir(), "consumer-update.json")) + s.Require().NoError(err) + + // // ensure we can opt in a non-allowlisted validator after the modification + _, err = s.Chain.Validators[3].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[3].Moniker, + "provider", "opt-in", consumerID) + s.Require().NoError(err) + validators, err = consumer.QueryJSON(s.GetContext(), "validators", "tendermint-validator-set") + s.Require().NoError(err) + s.Require().Equal(4, len(validators.Array())) +} + +func TestConsumerModification(t *testing.T) { + genesis := chainsuite.DefaultGenesis() + genesis = append(genesis, + cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", permissionlessDepositPeriod.String()), + ) + s := &ConsumerModificationSuite{ + Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + CreateRelayer: true, + Scope: chainsuite.ChainScopeTest, + UpgradeOnSetup: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + ChainConfig: ibc.ChainConfig{ + ModifyGenesis: cosmos.ModifyGenesis(genesis), + }, + }, + }), + consumerCfg: chainsuite.ConsumerConfig{ + ChainName: "ics-consumer", + Version: "v4.5.0", + ShouldCopyProviderKey: allProviderKeysCopied(), + Denom: chainsuite.Ucon, + TopN: 100, + AllowInactiveVals: true, + MinStake: 1_000_000, + Spec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + ChainConfig: ibc.ChainConfig{ + Images: []ibc.DockerImage{ + { + Repository: chainsuite.HyphaICSRepo, + Version: "v4.5.0", + UidGid: chainsuite.ICSUidGuid, + }, + }, + }, + }, + }, + } + suite.Run(t, s) +} diff --git a/tests/interchain/consumer_chain/mainnet_consumers_test.go b/tests/interchain/consumer_chain/mainnet_consumers_test.go new file mode 100644 index 00000000000..20188ad87e7 --- /dev/null +++ b/tests/interchain/consumer_chain/mainnet_consumers_test.go @@ -0,0 +1,59 @@ +package consumer_chain_test + +import ( + "testing" + + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/stretchr/testify/suite" + "golang.org/x/mod/semver" +) + +type MainnetConsumerChainsSuite struct { + *chainsuite.Suite +} + +func (s *MainnetConsumerChainsSuite) TestMainnetConsumerChainsAfterUpgrade() { + // We can't do these consumer launches yet because the chains aren't compatible with launching on v21 yet + if semver.Major(s.Env.OldGaiaImageVersion) == s.Env.UpgradeName && s.Env.UpgradeName == "v21" { + s.T().Skip("Skipping Consumer Launch tests when going from v21 -> v21") + } + neutron, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, chainsuite.ConsumerConfig{ + ChainName: "neutron", + Version: chainsuite.NeutronVersion, + ShouldCopyProviderKey: allProviderKeysCopied(), + Denom: chainsuite.NeutronDenom, + TopN: 95, + }) + s.Require().NoError(err) + stride, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, chainsuite.ConsumerConfig{ + ChainName: "stride", + Version: chainsuite.StrideVersion, + ShouldCopyProviderKey: allProviderKeysCopied(), + Denom: chainsuite.StrideDenom, + TopN: 95, + }) + s.Require().NoError(err) + + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), neutron, s.Relayer, 1_000_000, 0, 1)) + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), stride, s.Relayer, 1_000_000, 0, 1)) + + s.UpgradeChain() + + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), neutron, s.Relayer, 1_000_000, 0, 1)) + s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), stride, s.Relayer, 1_000_000, 0, 1)) + s.Require().NoError(chainsuite.SendSimpleIBCTx(s.GetContext(), s.Chain, neutron, s.Relayer)) + s.Require().NoError(chainsuite.SendSimpleIBCTx(s.GetContext(), s.Chain, stride, s.Relayer)) +} + +func TestMainnetConsumerChainsAfterUpgrade(t *testing.T) { + s := &MainnetConsumerChainsSuite{ + Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + CreateRelayer: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + }, + }), + } + suite.Run(t, s) +} diff --git a/tests/interchain/unbonding_test.go b/tests/interchain/consumer_chain/unbonding_test.go similarity index 99% rename from tests/interchain/unbonding_test.go rename to tests/interchain/consumer_chain/unbonding_test.go index 105d5dbbb57..9113d8f0b75 100644 --- a/tests/interchain/unbonding_test.go +++ b/tests/interchain/consumer_chain/unbonding_test.go @@ -1,4 +1,4 @@ -package interchain_test +package consumer_chain_test import ( "context" diff --git a/tests/interchain/delegator/authz_test.go b/tests/interchain/delegator/authz_test.go new file mode 100644 index 00000000000..ef8a01a0069 --- /dev/null +++ b/tests/interchain/delegator/authz_test.go @@ -0,0 +1,214 @@ +package delegator_test + +import ( + "context" + "fmt" + "path" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + "github.com/cosmos/gaia/v21/tests/interchain/delegator" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +const txAmount = 1_000_000_000 + +func txAmountUatom() string { + return fmt.Sprintf("%d%s", txAmount, chainsuite.Uatom) +} + +type AuthSuite struct { + *delegator.Suite +} + +func (s *AuthSuite) TestSend() { + balanceBefore, err := s.Chain.GetBalance(s.GetContext(), s.DelegatorWallet3.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "authz", "grant", s.DelegatorWallet2.FormattedAddress(), "send", + "--spend-limit", fmt.Sprintf("%d%s", txAmount*2, chainsuite.Uatom), + "--allow-list", s.DelegatorWallet3.FormattedAddress(), + ) + s.Require().NoError(err) + + s.Require().Error(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "bank", "send", s.DelegatorWallet.FormattedAddress(), s.DelegatorWallet2.FormattedAddress(), txAmountUatom())) + + s.Require().NoError(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "bank", "send", s.DelegatorWallet.FormattedAddress(), s.DelegatorWallet3.FormattedAddress(), txAmountUatom())) + balanceAfter, err := s.Chain.GetBalance(s.GetContext(), s.DelegatorWallet3.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + s.Require().Equal(balanceBefore.Add(sdkmath.NewInt(int64(txAmount))), balanceAfter) + + s.Require().Error(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "bank", "send", s.DelegatorWallet.FormattedAddress(), s.DelegatorWallet3.FormattedAddress(), fmt.Sprintf("%d%s", txAmount+200, chainsuite.Uatom))) + + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "authz", "revoke", s.DelegatorWallet2.FormattedAddress(), "/cosmos.bank.v1beta1.MsgSend", + ) + s.Require().NoError(err) + + s.Require().Error(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "bank", "send", s.DelegatorWallet.FormattedAddress(), s.DelegatorWallet3.FormattedAddress(), txAmountUatom())) +} + +func (s *AuthSuite) TestDelegate() { + _, err := s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "authz", "grant", s.DelegatorWallet2.FormattedAddress(), "delegate", + "--allowed-validators", s.Chain.ValidatorWallets[0].ValoperAddress, + ) + s.Require().NoError(err) + + s.Require().NoError(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "staking", "delegate", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), "--from", s.DelegatorWallet.FormattedAddress())) + s.Require().Error(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "staking", "delegate", s.Chain.ValidatorWallets[1].ValoperAddress, txAmountUatom(), "--from", s.DelegatorWallet.FormattedAddress())) + + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "authz", "revoke", s.DelegatorWallet2.FormattedAddress(), "/cosmos.staking.v1beta1.MsgDelegate", + ) + s.Require().NoError(err) + s.Require().Error(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "staking", "delegate", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), "--from", s.DelegatorWallet.FormattedAddress())) +} + +func (s *AuthSuite) TestUnbond() { + valHex, err := s.Chain.GetValidatorHex(s.GetContext(), 0) + s.Require().NoError(err) + powerBefore, err := s.Chain.GetValidatorPower(s.GetContext(), valHex) + s.Require().NoError(err) + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "staking", "delegate", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), + ) + s.Require().NoError(err) + s.Require().EventuallyWithT(func(c *assert.CollectT) { + powerAfter, err := s.Chain.GetValidatorPower(s.GetContext(), valHex) + s.Require().NoError(err) + assert.NoError(c, err) + assert.Greater(c, powerAfter, powerBefore) + }, 15*chainsuite.CommitTimeout, chainsuite.CommitTimeout) + + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "authz", "grant", s.DelegatorWallet2.FormattedAddress(), "unbond", + "--allowed-validators", s.Chain.ValidatorWallets[0].ValoperAddress, + ) + s.Require().NoError(err) + + s.Require().NoError(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "staking", "unbond", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), "--from", s.DelegatorWallet.FormattedAddress())) + s.Require().Error(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "staking", "unbond", s.Chain.ValidatorWallets[1].ValoperAddress, txAmountUatom(), "--from", s.DelegatorWallet.FormattedAddress())) + + s.Require().EventuallyWithT(func(c *assert.CollectT) { + powerAfter, err := s.Chain.GetValidatorPower(s.GetContext(), valHex) + s.Require().NoError(err) + assert.NoError(c, err) + assert.Equal(c, powerAfter, powerBefore) + }, 15*chainsuite.CommitTimeout, chainsuite.CommitTimeout) + + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "authz", "revoke", s.DelegatorWallet2.FormattedAddress(), "/cosmos.staking.v1beta1.MsgUndelegate", + ) + s.Require().NoError(err) + s.Require().Error(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "staking", "unbond", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), "--from", s.DelegatorWallet.FormattedAddress())) +} + +func (s AuthSuite) TestRedelegate() { + val0Hex, err := s.Chain.GetValidatorHex(s.GetContext(), 0) + s.Require().NoError(err) + val2Hex, err := s.Chain.GetValidatorHex(s.GetContext(), 1) + s.Require().NoError(err) + val0PowerBefore, err := s.Chain.GetValidatorPower(s.GetContext(), val0Hex) + s.Require().NoError(err) + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.Chain.ValidatorWallets[0].Moniker, + "staking", "delegate", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), + ) + s.Require().NoError(err) + s.Require().EventuallyWithT(func(c *assert.CollectT) { + val0PowerAfter, err := s.Chain.GetValidatorPower(s.GetContext(), val0Hex) + s.Require().NoError(err) + s.Require().NoError(err) + s.Require().Greater(val0PowerAfter, val0PowerBefore) + }, 15*chainsuite.CommitTimeout, chainsuite.CommitTimeout) + + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.Chain.ValidatorWallets[0].Address, + "authz", "grant", s.DelegatorWallet2.FormattedAddress(), "redelegate", + "--allowed-validators", s.Chain.ValidatorWallets[1].ValoperAddress, + ) + s.Require().NoError(err) + + s.Require().Error(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "staking", "redelegate", s.Chain.ValidatorWallets[1].ValoperAddress, s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) + + val2PowerBefore, err := s.Chain.GetValidatorPower(s.GetContext(), val2Hex) + s.Require().NoError(err) + s.Require().NoError(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "staking", "redelegate", s.Chain.ValidatorWallets[0].ValoperAddress, s.Chain.ValidatorWallets[1].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) + s.Require().EventuallyWithT(func(c *assert.CollectT) { + val2PowerAfter, err := s.Chain.GetValidatorPower(s.GetContext(), val2Hex) + s.Require().NoError(err) + s.Require().Greater(val2PowerAfter, val2PowerBefore) + }, 15*chainsuite.CommitTimeout, chainsuite.CommitTimeout) + + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.Chain.ValidatorWallets[0].Address, + "authz", "revoke", s.DelegatorWallet2.FormattedAddress(), "/cosmos.staking.v1beta1.MsgBeginRedelegate", + ) + s.Require().NoError(err) + + s.Require().Error(s.authzGenExec(s.GetContext(), s.DelegatorWallet2, "staking", "redelegate", s.Chain.ValidatorWallets[0].ValoperAddress, s.Chain.ValidatorWallets[1].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) +} + +func (s AuthSuite) TestGeneric() { + _, err := s.Chain.GetNode().ExecTx( + s.GetContext(), + s.Chain.ValidatorWallets[0].Moniker, + "authz", "grant", s.DelegatorWallet.FormattedAddress(), "generic", + "--msg-type", "/cosmos.gov.v1.MsgVote", + ) + s.Require().NoError(err) + + prop, err := s.Chain.BuildProposal(nil, "Test Proposal", "Test Proposal", "ipfs://CID", chainsuite.GovDepositAmount, "", false) + s.Require().NoError(err) + result, err := s.Chain.SubmitProposal(s.GetContext(), s.DelegatorWallet.FormattedAddress(), prop) + s.Require().NoError(err) + s.Require().NoError(s.authzGenExec(s.GetContext(), s.DelegatorWallet, "gov", "vote", result.ProposalID, "yes", "--from", s.Chain.ValidatorWallets[0].Address)) +} + +func TestAuthz(t *testing.T) { + two := 2 + s := &AuthSuite{Suite: &delegator.Suite{Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + UpgradeOnSetup: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &two, + }, + })}} + suite.Run(t, s) +} + +func (s AuthSuite) authzGenExec(ctx context.Context, grantee ibc.Wallet, command ...string) error { + txjson, err := s.Chain.GenerateTx(ctx, 1, command...) + s.Require().NoError(err) + + err = s.Chain.GetNode().WriteFile(ctx, []byte(txjson), "tx.json") + s.Require().NoError(err) + + _, err = s.Chain.GetNode().ExecTx( + ctx, + grantee.FormattedAddress(), + "authz", "exec", path.Join(s.Chain.Validators[1].HomeDir(), "tx.json"), + ) + return err +} diff --git a/tests/interchain/delegator/bank_test.go b/tests/interchain/delegator/bank_test.go new file mode 100644 index 00000000000..b19022c63b4 --- /dev/null +++ b/tests/interchain/delegator/bank_test.go @@ -0,0 +1,39 @@ +package delegator_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + "github.com/cosmos/gaia/v21/tests/interchain/delegator" + "github.com/stretchr/testify/suite" +) + +type BankSuite struct { + *delegator.Suite +} + +func (s *BankSuite) TestSend() { + balanceBefore, err := s.Chain.GetBalance(s.GetContext(), s.DelegatorWallet2.FormattedAddress(), s.Chain.Config().Denom) + s.Require().NoError(err) + + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "bank", "send", + s.DelegatorWallet.FormattedAddress(), s.DelegatorWallet2.FormattedAddress(), txAmountUatom(), + ) + s.Require().NoError(err) + + balanceAfter, err := s.Chain.GetBalance(s.GetContext(), s.DelegatorWallet2.FormattedAddress(), s.Chain.Config().Denom) + s.Require().NoError(err) + s.Require().Equal(balanceBefore.Add(sdkmath.NewInt(txAmount)), balanceAfter) + +} + +func TestBank(t *testing.T) { + s := &BankSuite{Suite: &delegator.Suite{Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + UpgradeOnSetup: true, + })}} + suite.Run(t, s) +} diff --git a/tests/interchain/cosmwasm_test.go b/tests/interchain/delegator/cosmwasm_test.go similarity index 87% rename from tests/interchain/cosmwasm_test.go rename to tests/interchain/delegator/cosmwasm_test.go index fa0d8fbf776..725b29e11c3 100644 --- a/tests/interchain/cosmwasm_test.go +++ b/tests/interchain/delegator/cosmwasm_test.go @@ -1,4 +1,4 @@ -package interchain_test +package delegator_test import ( "fmt" @@ -7,12 +7,12 @@ import ( "testing" "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" - "github.com/strangelove-ventures/interchaintest/v8" + "github.com/cosmos/gaia/v21/tests/interchain/delegator" "github.com/stretchr/testify/suite" ) type CosmWasmSuite struct { - *chainsuite.Suite + *delegator.Suite ContractWasm []byte ContractPath string PreUpgradeContractCode string @@ -61,7 +61,7 @@ func (s *CosmWasmSuite) TestCantStoreWithoutProp() { s.Require().NoError(err) codeCountBefore := len(infos.Array()) - _, err = s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.DelegatorWallet.FormattedAddress(), "wasm", "store", s.ContractPath, ) s.Require().Error(err) @@ -73,7 +73,7 @@ func (s *CosmWasmSuite) TestCantStoreWithoutProp() { } func (s *CosmWasmSuite) TestCantInstantiateWithoutProp() { - _, err := s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, + _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.DelegatorWallet.FormattedAddress(), "wasm", "instantiate", s.PreUpgradeContractCode, initState, "--label", "my-contract", "--no-admin", ) s.Require().Error(err) @@ -97,14 +97,14 @@ func (s *CosmWasmSuite) TestCreateNewContract() { } func (s *CosmWasmSuite) executeContractByTx(contractAddr string) { - _, err := s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, + _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.DelegatorWallet.FormattedAddress(), "wasm", "execute", contractAddr, increment, ) s.Require().NoError(err) } func (s *CosmWasmSuite) executeContractByProposal(contractAddr string) { - txhash, err := s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, + txhash, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.DelegatorWallet.FormattedAddress(), "wasm", "submit-proposal", "execute-contract", contractAddr, increment, "--title", "Increment count", @@ -131,7 +131,7 @@ func (s *CosmWasmSuite) storeInstantiateProposal(initState string) (string, stri govAddr, err := s.Chain.GetGovernanceAddress(s.GetContext()) s.Require().NoError(err) - txhash, err := s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, + txhash, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.DelegatorWallet.FormattedAddress(), "wasm", "submit-proposal", "store-instantiate", s.ContractPath, initState, "--label", "my-contract", @@ -160,7 +160,7 @@ func (s *CosmWasmSuite) storeInstantiateProposal(initState string) (string, stri func TestCosmWasm(t *testing.T) { s := &CosmWasmSuite{ - Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{UpgradeOnSetup: false}), + Suite: &delegator.Suite{Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{UpgradeOnSetup: false})}, } suite.Run(t, s) } diff --git a/tests/interchain/delegator/feegrant_test.go b/tests/interchain/delegator/feegrant_test.go new file mode 100644 index 00000000000..e03cc784dce --- /dev/null +++ b/tests/interchain/delegator/feegrant_test.go @@ -0,0 +1,97 @@ +package delegator_test + +import ( + "testing" + "time" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + "github.com/cosmos/gaia/v21/tests/interchain/delegator" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "github.com/stretchr/testify/suite" +) + +type FeegrantSuite struct { + *delegator.Suite +} + +func (s *FeegrantSuite) TestFeegrant() { + var ( + granter = s.DelegatorWallet + grantee = s.DelegatorWallet2 + fundsReceiver = s.DelegatorWallet3 + ) + + tests := []struct { + name string + revoke func(expireTime time.Time) + }{ + { + name: "revoke", + revoke: func(_ time.Time) { + _, err := s.Chain.GetNode().ExecTx( + s.GetContext(), + granter.FormattedAddress(), + "feegrant", "revoke", granter.FormattedAddress(), grantee.FormattedAddress(), + ) + s.Require().NoError(err) + }, + }, + { + name: "expire", + revoke: func(expire time.Time) { + <-time.After(time.Until(expire)) + err := testutil.WaitForBlocks(s.GetContext(), 1, s.Chain) + s.Require().NoError(err) + }, + }, + } + + for _, tt := range tests { + tt := tt + s.Run(tt.name, func() { + expire := time.Now().Add(20 * chainsuite.CommitTimeout) + _, err := s.Chain.GetNode().ExecTx( + s.GetContext(), + granter.FormattedAddress(), + "feegrant", "grant", granter.FormattedAddress(), grantee.FormattedAddress(), + "--expiration", expire.Format(time.RFC3339), + ) + s.Require().NoError(err) + + granterBalanceBefore, err := s.Chain.GetBalance(s.GetContext(), granter.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + granteeBalanceBefore, err := s.Chain.GetBalance(s.GetContext(), grantee.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), grantee.FormattedAddress(), + "bank", "send", grantee.FormattedAddress(), fundsReceiver.FormattedAddress(), txAmountUatom(), + "--fee-granter", granter.FormattedAddress(), + ) + s.Require().NoError(err) + + granteeBalanceAfter, err := s.Chain.GetBalance(s.GetContext(), grantee.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + granterBalanceAfter, err := s.Chain.GetBalance(s.GetContext(), granter.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + + s.Require().True(granterBalanceAfter.LT(granterBalanceBefore), "granterBalanceBefore: %s, granterBalanceAfter: %s", granterBalanceBefore, granterBalanceAfter) + s.Require().True(granteeBalanceAfter.Equal(granteeBalanceBefore.Sub(sdkmath.NewInt(txAmount))), "granteeBalanceBefore: %s, granteeBalanceAfter: %s", granteeBalanceBefore, granteeBalanceAfter) + + tt.revoke(expire) + + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), grantee.FormattedAddress(), + "bank", "send", grantee.FormattedAddress(), fundsReceiver.FormattedAddress(), txAmountUatom(), + "--fee-granter", granter.FormattedAddress(), + ) + s.Require().Error(err) + }) + } +} + +func TestFeegrant(t *testing.T) { + s := &FeegrantSuite{Suite: &delegator.Suite{Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + UpgradeOnSetup: true, + })}} + suite.Run(t, s) +} diff --git a/tests/interchain/delegator/ica_test.go b/tests/interchain/delegator/ica_test.go new file mode 100644 index 00000000000..d2bf4dfc309 --- /dev/null +++ b/tests/interchain/delegator/ica_test.go @@ -0,0 +1,115 @@ +package delegator_test + +import ( + "context" + "fmt" + "strings" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + "github.com/cosmos/gaia/v21/tests/interchain/delegator" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type ICAControllerSuite struct { + *delegator.Suite + Host *chainsuite.Chain +} + +func (s *ICAControllerSuite) SetupSuite() { + s.Suite.SetupSuite() + host, err := s.Chain.AddLinkedChain(s.GetContext(), s.T(), s.Relayer, chainsuite.DefaultChainSpec(s.Env)) + s.Require().NoError(err) + s.Host = host +} + +func (s *ICAControllerSuite) TestNonGovICA() { + const amountToSend = int64(3_300_000_000) + + srcAddress := s.DelegatorWallet.FormattedAddress() + srcChannel, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, s.Host) + s.Require().NoError(err) + + var icaAddress string + s.Run("Register ICA account", func() { + icaAddress, err = s.Chain.SetupICAAccount(s.GetContext(), s.Host, s.Relayer, srcAddress, 0, amountToSend) + s.Require().NoError(err) + + _, err = s.Chain.SendIBCTransfer(s.GetContext(), srcChannel.ChannelID, srcAddress, ibc.WalletAmount{ + Address: icaAddress, + Amount: sdkmath.NewInt(amountToSend), + Denom: s.Chain.Config().Denom, + }, ibc.TransferOptions{}) + s.Require().NoError(err) + }) + + s.Run("Generate and send ICA transaction", func() { + wallets := s.Host.ValidatorWallets + s.Require().NoError(err) + dstAddress := wallets[0].Address + + var ibcStakeDenom string + s.Require().EventuallyWithT(func(c *assert.CollectT) { + balances, err := s.Host.BankQueryAllBalances(s.GetContext(), icaAddress) + s.Require().NoError(err) + s.Require().NotEmpty(balances) + for _, c := range balances { + if strings.Contains(c.Denom, "ibc") { + ibcStakeDenom = c.Denom + break + } + } + assert.NotEmpty(c, ibcStakeDenom) + }, 10*chainsuite.CommitTimeout, chainsuite.CommitTimeout) + + recipientBalanceBefore, err := s.Host.GetBalance(s.GetContext(), dstAddress, ibcStakeDenom) + s.Require().NoError(err) + + icaAmount := int64(amountToSend / 3) + srcConnection := srcChannel.ConnectionHops[0] + + s.Require().NoError(s.sendICATx(s.GetContext(), 0, srcAddress, dstAddress, icaAddress, srcConnection, icaAmount, ibcStakeDenom)) + s.Relayer.ClearTransferChannel(s.GetContext(), s.Chain, s.Host) + + s.Require().EventuallyWithT(func(c *assert.CollectT) { + recipientBalanceAfter, err := s.Host.GetBalance(s.GetContext(), dstAddress, ibcStakeDenom) + assert.NoError(c, err) + + assert.Equal(c, recipientBalanceBefore.Add(sdkmath.NewInt(icaAmount)), recipientBalanceAfter) + }, 10*chainsuite.CommitTimeout, chainsuite.CommitTimeout) + }) +} + +func TestDelegatorICA(t *testing.T) { + s := &ICAControllerSuite{Suite: &delegator.Suite{Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + UpgradeOnSetup: true, + CreateRelayer: true, + })}} + suite.Run(t, s) +} + +func (s *ICAControllerSuite) sendICATx(ctx context.Context, valIdx int, srcAddress string, dstAddress string, icaAddress string, srcConnection string, amount int64, denom string) error { + jsonBankSend := fmt.Sprintf(`{"@type": "/cosmos.bank.v1beta1.MsgSend", "from_address":"%s","to_address":"%s","amount":[{"denom":"%s","amount":"%d"}]}`, icaAddress, dstAddress, denom, amount) + + msgBz, _, err := s.Chain.GetNode().Exec(ctx, []string{"gaiad", "tx", "ica", "host", "generate-packet-data", string(jsonBankSend), "--encoding", "proto3"}, nil) + if err != nil { + return err + } + + msgPath := "msg.json" + if err := s.Chain.Validators[valIdx].WriteFile(ctx, msgBz, msgPath); err != nil { + return err + } + msgPath = s.Chain.Validators[valIdx].HomeDir() + "/" + msgPath + _, err = s.Chain.Validators[valIdx].ExecTx(ctx, srcAddress, + "interchain-accounts", "controller", "send-tx", + srcConnection, msgPath, + ) + if err != nil { + return err + } + return nil +} diff --git a/tests/interchain/lsm_test.go b/tests/interchain/delegator/lsm_test.go similarity index 96% rename from tests/interchain/lsm_test.go rename to tests/interchain/delegator/lsm_test.go index d99d5d3e482..50c32d19da0 100644 --- a/tests/interchain/lsm_test.go +++ b/tests/interchain/delegator/lsm_test.go @@ -1,4 +1,4 @@ -package interchain_test +package delegator_test import ( "encoding/json" @@ -231,14 +231,18 @@ func (s *LSMSuite) TestLSMHappyPath() { s.checkAMinusBEqualsX(happyLiquid3DelegationBalance, "0", sdkmath.NewInt(ibcTransfer)) }) s.Run("Cleanup", func() { - _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.LSMWallets[lsmBondingMoniker].FormattedAddress(), + validatorBondSharesBeforeResult, err := s.Chain.QueryJSON(s.GetContext(), "validator.validator_bond_shares", "staking", "validator", providerWallet.ValoperAddress) + s.Require().NoError(err) + validatorBondSharesBefore := validatorBondSharesBeforeResult.String() + + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.LSMWallets[lsmBondingMoniker].FormattedAddress(), "staking", "unbond", providerWallet.ValoperAddress, fmt.Sprintf("%d%s", delegation, s.Chain.Config().Denom)) s.Require().NoError(err) validatorBondSharesResult, err := s.Chain.QueryJSON(s.GetContext(), "validator.validator_bond_shares", "staking", "validator", providerWallet.ValoperAddress) s.Require().NoError(err) validatorBondShares := validatorBondSharesResult.String() - s.checkAMinusBEqualsX(validatorBondShares, "0", sdkmath.NewInt(0).Mul(s.ShareFactor)) + s.checkAMinusBEqualsX(validatorBondSharesBefore, validatorBondShares, sdkmath.NewInt(delegation).Mul(s.ShareFactor)) _, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.LSMWallets[lsmLiquid1Moniker].FormattedAddress(), "staking", "unbond", providerWallet.ValoperAddress, fmt.Sprintf("%s%s", happyLiquid1DelegationBalance, s.Chain.Config().Denom)) @@ -267,7 +271,7 @@ func (s *LSMSuite) TestICADelegate() { }) s.Require().NoError(err) - providerWallet := s.Chain.ValidatorWallets[1] + providerWallet := s.Chain.ValidatorWallets[0] strideWallet := s.Stride.ValidatorWallets[0] @@ -462,10 +466,11 @@ func (s *LSMSuite) SetupSuite() { s.T().Skip("Skipping LSM when going from v21 -> v21") } stride, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, chainsuite.ConsumerConfig{ - ChainName: "stride", - Version: chainsuite.StrideVersion, - Denom: chainsuite.StrideDenom, - TopN: 100, + ChainName: "stride", + Version: chainsuite.StrideVersion, + Denom: chainsuite.StrideDenom, + TopN: 100, + ShouldCopyProviderKey: []bool{true}, }) s.Require().NoError(err) s.Stride = stride diff --git a/tests/interchain/delegator/multisig_test.go b/tests/interchain/delegator/multisig_test.go new file mode 100644 index 00000000000..478c0b6eba4 --- /dev/null +++ b/tests/interchain/delegator/multisig_test.go @@ -0,0 +1,108 @@ +package delegator_test + +import ( + "fmt" + "path" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + "github.com/cosmos/gaia/v21/tests/interchain/delegator" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/stretchr/testify/suite" +) + +type MultisigTest struct { + *delegator.Suite +} + +func (s *MultisigTest) TestMultisig() { + multisigName := "multisig" + _, _, err := s.Chain.GetNode().ExecBin( + s.GetContext(), "keys", "add", multisigName, + "--multisig", fmt.Sprintf("%s,%s,%s", s.DelegatorWallet.KeyName(), s.DelegatorWallet2.KeyName(), s.DelegatorWallet3.KeyName()), + "--multisig-threshold", "2", "--keyring-backend", "test", + ) + s.Require().NoError(err) + bogusWallet, err := s.Chain.BuildWallet(s.GetContext(), "bogus", "") + s.Require().NoError(err) + + multisigAddr, err := s.Chain.GetNode().KeyBech32(s.GetContext(), multisigName, "") + s.Require().NoError(err) + + err = s.Chain.SendFunds(s.GetContext(), interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Denom: chainsuite.Uatom, + Amount: sdkmath.NewInt(chainsuite.ValidatorFunds), + Address: multisigAddr, + }) + s.Require().NoError(err) + + balanceBefore, err := s.Chain.GetBalance(s.GetContext(), s.DelegatorWallet3.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + + txjson, err := s.Chain.GenerateTx( + s.GetContext(), 0, "bank", "send", multisigName, s.DelegatorWallet3.FormattedAddress(), txAmountUatom(), + "--gas", "auto", "--gas-adjustment", fmt.Sprint(s.Chain.Config().GasAdjustment), "--gas-prices", s.Chain.Config().GasPrices, + ) + s.Require().NoError(err) + + err = s.Chain.GetNode().WriteFile(s.GetContext(), []byte(txjson), "tx.json") + s.Require().NoError(err) + + signed0, _, err := s.Chain.GetNode().Exec(s.GetContext(), + s.Chain.GetNode().TxCommand(s.DelegatorWallet.KeyName(), + "sign", + path.Join(s.Chain.GetNode().HomeDir(), "tx.json"), + "--multisig", multisigAddr, + ), nil) + s.Require().NoError(err) + + signed1, _, err := s.Chain.GetNode().Exec(s.GetContext(), + s.Chain.GetNode().TxCommand(s.DelegatorWallet2.KeyName(), + "sign", + path.Join(s.Chain.GetNode().HomeDir(), "tx.json"), + "--multisig", multisigAddr, + ), nil) + s.Require().NoError(err) + + _, _, err = s.Chain.GetNode().Exec(s.GetContext(), + s.Chain.GetNode().TxCommand(bogusWallet.KeyName(), + "sign", + path.Join(s.Chain.GetNode().HomeDir(), "tx.json"), + "--multisig", multisigAddr, + ), nil) + s.Require().Error(err) + + err = s.Chain.GetNode().WriteFile(s.GetContext(), signed0, "signed0.json") + s.Require().NoError(err) + err = s.Chain.GetNode().WriteFile(s.GetContext(), signed1, "signed1.json") + s.Require().NoError(err) + + multisign, _, err := s.Chain.GetNode().Exec(s.GetContext(), s.Chain.GetNode().TxCommand( + multisigName, + "multisign", + path.Join(s.Chain.GetNode().HomeDir(), "tx.json"), + multisigName, + path.Join(s.Chain.GetNode().HomeDir(), "signed0.json"), + path.Join(s.Chain.GetNode().HomeDir(), "signed1.json"), + ), nil) + s.Require().NoError(err) + + err = s.Chain.GetNode().WriteFile(s.GetContext(), multisign, "multisign.json") + s.Require().NoError(err) + + _, err = s.Chain.GetNode().ExecTx(s.GetContext(), multisigName, "broadcast", path.Join(s.Chain.GetNode().HomeDir(), "multisign.json")) + s.Require().NoError(err) + + balanceAfter, err := s.Chain.GetBalance(s.GetContext(), s.DelegatorWallet3.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + s.Require().Equal(balanceBefore.Add(sdkmath.NewInt(txAmount)), balanceAfter) +} + +func TestMultisig(t *testing.T) { + s := &MultisigTest{Suite: &delegator.Suite{Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + UpgradeOnSetup: true, + })}} + suite.Run(t, s) +} diff --git a/tests/interchain/pfm_test.go b/tests/interchain/delegator/pfm_test.go similarity index 73% rename from tests/interchain/pfm_test.go rename to tests/interchain/delegator/pfm_test.go index 867102bb0b0..d7a71bd6575 100644 --- a/tests/interchain/pfm_test.go +++ b/tests/interchain/delegator/pfm_test.go @@ -1,19 +1,24 @@ -package interchain_test +package delegator_test import ( "encoding/json" "testing" + sdkmath "cosmossdk.io/math" "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + "github.com/cosmos/gaia/v21/tests/interchain/delegator" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/ibc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) type PFMSuite struct { - *chainsuite.Suite - Chains []*chainsuite.Chain + *delegator.Suite + Chains []*chainsuite.Chain + ADelegatorWallet ibc.Wallet + DDelegatorWallet ibc.Wallet } func (s *PFMSuite) SetupSuite() { @@ -26,6 +31,16 @@ func (s *PFMSuite) SetupSuite() { s.Require().NoError(err) s.Chains = []*chainsuite.Chain{s.Chain, chainB, chainC, chainD} + s.ADelegatorWallet = s.DelegatorWallet + wallet, err := chainD.BuildWallet(s.GetContext(), "delegator", "") + s.Require().NoError(err) + s.DDelegatorWallet = wallet + err = chainD.SendFunds(s.GetContext(), interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: s.DDelegatorWallet.FormattedAddress(), + Amount: sdkmath.NewInt(100_000_000_000), + Denom: chainD.Config().Denom, + }) + s.Require().NoError(err) } func (s *PFMSuite) TestPFMHappyPath() { @@ -50,11 +65,7 @@ func (s *PFMSuite) TestPFMHappyPath() { } targetDenomDA = transfertypes.ParseDenomTrace(targetDenomDA).IBCDenom() - dWallet1 := s.Chains[3].ValidatorWallets[0] - - aWallet1 := s.Chains[0].ValidatorWallets[0] - - dStartBalance, err := s.Chains[3].GetBalance(s.GetContext(), dWallet1.Address, targetDenomAD) + dStartBalance, err := s.Chains[3].GetBalance(s.GetContext(), s.DDelegatorWallet.FormattedAddress(), targetDenomAD) s.Require().NoError(err) timeout := "10m" @@ -66,7 +77,7 @@ func (s *PFMSuite) TestPFMHappyPath() { "timeout": timeout, "next": map[string]interface{}{ "forward": map[string]interface{}{ - "receiver": dWallet1.Address, + "receiver": s.DDelegatorWallet.FormattedAddress(), "port": "transfer", "channel": forwardChannels[2].ChannelID, "timeout": timeout, @@ -76,19 +87,19 @@ func (s *PFMSuite) TestPFMHappyPath() { } memoBytes, err := json.Marshal(memo) s.Require().NoError(err) - _, err = s.Chains[0].GetNode().ExecTx(s.GetContext(), aWallet1.Address, + _, err = s.Chains[0].GetNode().ExecTx(s.GetContext(), s.ADelegatorWallet.FormattedAddress(), "ibc-transfer", "transfer", "transfer", forwardChannels[0].ChannelID, "pfm", "1000000"+s.Chains[0].Config().Denom, "--memo", string(memoBytes)) s.Require().NoError(err) s.Require().EventuallyWithT(func(c *assert.CollectT) { - dEndBalance, err := s.Chains[3].GetBalance(s.GetContext(), dWallet1.Address, targetDenomAD) + dEndBalance, err := s.Chains[3].GetBalance(s.GetContext(), s.DDelegatorWallet.FormattedAddress(), targetDenomAD) assert.NoError(c, err) assert.Truef(c, dEndBalance.Sub(dStartBalance).IsPositive(), "expected %d - %d > 0 (it was %d) in %s", dEndBalance, dStartBalance, dEndBalance.Sub(dStartBalance), targetDenomAD) }, 30*chainsuite.CommitTimeout, chainsuite.CommitTimeout, "chain D balance has not increased") - aStartBalance, err := s.Chains[0].GetBalance(s.GetContext(), aWallet1.Address, targetDenomDA) + aStartBalance, err := s.Chains[0].GetBalance(s.GetContext(), s.ADelegatorWallet.FormattedAddress(), targetDenomDA) s.Require().NoError(err) memo = map[string]interface{}{ @@ -99,7 +110,7 @@ func (s *PFMSuite) TestPFMHappyPath() { "timeout": timeout, "next": map[string]interface{}{ "forward": map[string]interface{}{ - "receiver": aWallet1.Address, + "receiver": s.ADelegatorWallet.FormattedAddress(), "port": "transfer", "channel": backwardChannels[0].ChannelID, "timeout": timeout, @@ -109,13 +120,13 @@ func (s *PFMSuite) TestPFMHappyPath() { } memoBytes, err = json.Marshal(memo) s.Require().NoError(err) - _, err = s.Chains[3].GetNode().ExecTx(s.GetContext(), dWallet1.Address, + _, err = s.Chains[3].GetNode().ExecTx(s.GetContext(), s.DDelegatorWallet.FormattedAddress(), "ibc-transfer", "transfer", "transfer", backwardChannels[2].ChannelID, "pfm", "1000000"+s.Chains[3].Config().Denom, "--memo", string(memoBytes)) s.Require().NoError(err) s.Require().EventuallyWithT(func(c *assert.CollectT) { - aEndBalance, err := s.Chains[0].GetBalance(s.GetContext(), aWallet1.Address, targetDenomDA) + aEndBalance, err := s.Chains[0].GetBalance(s.GetContext(), s.ADelegatorWallet.FormattedAddress(), targetDenomDA) assert.NoError(c, err) assert.Truef(c, aEndBalance.Sub(aStartBalance).IsPositive(), "expected %d - %d > 0 (it was %d) in %s", aEndBalance, aStartBalance, aEndBalance.Sub(aStartBalance), targetDenomDA) @@ -125,9 +136,9 @@ func (s *PFMSuite) TestPFMHappyPath() { func TestPFM(t *testing.T) { s := &PFMSuite{ - Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + Suite: &delegator.Suite{Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ UpgradeOnSetup: true, CreateRelayer: true, - })} + })}} suite.Run(t, s) } diff --git a/tests/interchain/delegator/staking_test.go b/tests/interchain/delegator/staking_test.go new file mode 100644 index 00000000000..f2c99eb5923 --- /dev/null +++ b/tests/interchain/delegator/staking_test.go @@ -0,0 +1,53 @@ +package delegator_test + +import ( + "testing" + "time" + + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + "github.com/cosmos/gaia/v21/tests/interchain/delegator" + "github.com/stretchr/testify/suite" +) + +type StakingSuite struct { + *delegator.Suite +} + +func (s *StakingSuite) TestDelegateWithdrawUnbond() { + // delegate tokens + _, err := s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "staking", "delegate", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), + ) + s.Require().NoError(err) + + startingBalance, err := s.Chain.GetBalance(s.GetContext(), s.DelegatorWallet.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + time.Sleep(20 * time.Second) + // Withdraw rewards + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, + ) + s.Require().NoError(err) + endingBalance, err := s.Chain.GetBalance(s.GetContext(), s.DelegatorWallet.FormattedAddress(), chainsuite.Uatom) + s.Require().NoError(err) + s.Require().Truef(endingBalance.GT(startingBalance), "endingBalance: %s, startingBalance: %s", endingBalance, startingBalance) + + // Unbond tokens + _, err = s.Chain.GetNode().ExecTx( + s.GetContext(), + s.DelegatorWallet.FormattedAddress(), + "staking", "unbond", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), + ) + s.Require().NoError(err) +} + +func TestStaking(t *testing.T) { + s := &StakingSuite{Suite: &delegator.Suite{Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + UpgradeOnSetup: true, + })}} + suite.Run(t, s) +} diff --git a/tests/interchain/delegator/suite.go b/tests/interchain/delegator/suite.go new file mode 100644 index 00000000000..972482b339d --- /dev/null +++ b/tests/interchain/delegator/suite.go @@ -0,0 +1,48 @@ +package delegator + +import ( + sdkmath "cosmossdk.io/math" + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" +) + +// Suite is a common base suite for all delegator tests. +type Suite struct { + *chainsuite.Suite + DelegatorWallet ibc.Wallet + DelegatorWallet2 ibc.Wallet + DelegatorWallet3 ibc.Wallet +} + +func (s *Suite) SetupSuite() { + s.Suite.SetupSuite() + wallet, err := s.Chain.BuildWallet(s.GetContext(), "delegator", "") + s.Require().NoError(err) + s.DelegatorWallet = wallet + s.Require().NoError(s.Chain.SendFunds(s.GetContext(), interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: s.DelegatorWallet.FormattedAddress(), + Amount: sdkmath.NewInt(100_000_000_000), + Denom: s.Chain.Config().Denom, + })) + + wallet, err = s.Chain.BuildWallet(s.GetContext(), "delegator2", "") + s.Require().NoError(err) + s.DelegatorWallet2 = wallet + + wallet, err = s.Chain.BuildWallet(s.GetContext(), "delegator3", "") + s.Require().NoError(err) + s.DelegatorWallet3 = wallet + + s.Require().NoError(s.Chain.SendFunds(s.GetContext(), interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: s.DelegatorWallet2.FormattedAddress(), + Amount: sdkmath.NewInt(100_000_000_000), + Denom: s.Chain.Config().Denom, + })) + + s.Require().NoError(s.Chain.SendFunds(s.GetContext(), interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Address: s.DelegatorWallet3.FormattedAddress(), + Amount: sdkmath.NewInt(100_000_000_000), + Denom: s.Chain.Config().Denom, + })) +} diff --git a/tests/interchain/testdata/contract.wasm b/tests/interchain/delegator/testdata/contract.wasm similarity index 100% rename from tests/interchain/testdata/contract.wasm rename to tests/interchain/delegator/testdata/contract.wasm diff --git a/tests/interchain/feemarket_test.go b/tests/interchain/feemarket_test.go index 473915832ac..06097cb1d6f 100644 --- a/tests/interchain/feemarket_test.go +++ b/tests/interchain/feemarket_test.go @@ -215,6 +215,9 @@ func TestFeemarket(t *testing.T) { s := &FeemarketSuite{ Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ UpgradeOnSetup: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + }, }), } suite.Run(t, s) diff --git a/tests/interchain/go.mod b/tests/interchain/go.mod index d116fea5d8c..6d0d0fdeb04 100644 --- a/tests/interchain/go.mod +++ b/tests/interchain/go.mod @@ -23,7 +23,6 @@ require ( cosmossdk.io/math v1.3.0 github.com/cometbft/cometbft v0.38.11 github.com/cosmos/cosmos-sdk v0.50.9 - github.com/cosmos/gogoproto v1.7.0 github.com/cosmos/ibc-go/v8 v8.5.0 github.com/cosmos/interchain-security/v5 v5.1.1 github.com/docker/docker v27.1.2+incompatible @@ -96,6 +95,7 @@ require ( github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gogoproto v1.7.0 // indirect github.com/cosmos/iavl v1.3.0 // indirect github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect github.com/cosmos/ics23/go v0.11.0 // indirect diff --git a/tests/interchain/ica_controller_test.go b/tests/interchain/ica_controller_test.go deleted file mode 100644 index 3e972bfcd4e..00000000000 --- a/tests/interchain/ica_controller_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package interchain_test - -import ( - "context" - "encoding/json" - "strings" - "testing" - - sdkmath "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" - "github.com/cosmos/gogoproto/proto" - icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types" - "github.com/strangelove-ventures/interchaintest/v8" - "github.com/strangelove-ventures/interchaintest/v8/ibc" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type ICAControllerSuite struct { - *chainsuite.Suite - Host *chainsuite.Chain -} - -func (s *ICAControllerSuite) SetupSuite() { - s.Suite.SetupSuite() - host, err := s.Chain.AddLinkedChain(s.GetContext(), s.T(), s.Relayer, chainsuite.DefaultChainSpec(s.Env)) - s.Require().NoError(err) - s.Host = host -} - -func (s *ICAControllerSuite) TestICAController() { - const amountToSend = int64(3_300_000_000) - wallets := s.Chain.ValidatorWallets - valIdx := 0 - - var icaAddress, srcAddress string - var err error - for ; valIdx < len(wallets); valIdx++ { - srcAddress = wallets[valIdx].Address - icaAddress, err = s.Chain.SetupICAAccount(s.GetContext(), s.Host, s.Relayer, srcAddress, valIdx, amountToSend) - if err == nil { - break - } else if strings.Contains(err.Error(), "active channel already set for this owner") { - chainsuite.GetLogger(s.GetContext()).Sugar().Warnf("error setting up ICA account: %s", err) - valIdx++ - continue - } - // if we get here, fail the test. Unexpected error. - s.Require().NoError(err) - } - if icaAddress == "" { - // this'll happen if every validator has an ICA account already - s.Require().Fail("unable to create ICA account") - } - - srcChannel, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, s.Host) - s.Require().NoError(err) - - _, err = s.Chain.SendIBCTransfer(s.GetContext(), srcChannel.ChannelID, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ - Address: icaAddress, - Amount: sdkmath.NewInt(amountToSend), - Denom: s.Chain.Config().Denom, - }, ibc.TransferOptions{}) - s.Require().NoError(err) - - wallets = s.Host.ValidatorWallets - s.Require().NoError(err) - dstAddress := wallets[0].Address - - var ibcStakeDenom string - s.Require().EventuallyWithT(func(c *assert.CollectT) { - balances, err := s.Host.BankQueryAllBalances(s.GetContext(), icaAddress) - s.Require().NoError(err) - s.Require().NotEmpty(balances) - for _, c := range balances { - if strings.Contains(c.Denom, "ibc") { - ibcStakeDenom = c.Denom - break - } - } - assert.NotEmpty(c, ibcStakeDenom) - }, 10*chainsuite.CommitTimeout, chainsuite.CommitTimeout) - - recipientBalanceBefore, err := s.Host.GetBalance(s.GetContext(), dstAddress, ibcStakeDenom) - s.Require().NoError(err) - - icaAmount := int64(amountToSend / 3) - - srcConnection := srcChannel.ConnectionHops[0] - - s.Require().NoError(s.sendICATx(s.GetContext(), valIdx, srcAddress, dstAddress, icaAddress, srcConnection, icaAmount, ibcStakeDenom)) - - s.Require().EventuallyWithT(func(c *assert.CollectT) { - recipientBalanceAfter, err := s.Host.GetBalance(s.GetContext(), dstAddress, ibcStakeDenom) - assert.NoError(c, err) - - assert.Equal(c, recipientBalanceBefore.Add(sdkmath.NewInt(icaAmount)), recipientBalanceAfter) - }, 10*chainsuite.CommitTimeout, chainsuite.CommitTimeout) - -} - -func TestICAController(t *testing.T) { - s := &ICAControllerSuite{Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ - UpgradeOnSetup: true, - CreateRelayer: true, - })} - suite.Run(t, s) -} - -func (s *ICAControllerSuite) sendICATx(ctx context.Context, valIdx int, srcAddress string, dstAddress string, icaAddress string, srcConnection string, amount int64, denom string) error { - interfaceRegistry := codectypes.NewInterfaceRegistry() - cdc := codec.NewProtoCodec(interfaceRegistry) - - bankSendMsg := banktypes.NewMsgSend( - sdk.MustAccAddressFromBech32(icaAddress), - sdk.MustAccAddressFromBech32(dstAddress), - sdk.NewCoins(sdk.NewCoin(denom, sdkmath.NewInt(amount))), - ) - data, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{bankSendMsg}, icatypes.EncodingProtobuf) - if err != nil { - return err - } - - msg, err := json.Marshal(icatypes.InterchainAccountPacketData{ - Type: icatypes.EXECUTE_TX, - Data: data, - }) - if err != nil { - return err - } - msgPath := "msg.json" - if err := s.Chain.Validators[valIdx].WriteFile(ctx, msg, msgPath); err != nil { - return err - } - msgPath = s.Chain.Validators[valIdx].HomeDir() + "/" + msgPath - _, err = s.Chain.Validators[valIdx].ExecTx(ctx, srcAddress, - "interchain-accounts", "controller", "send-tx", - srcConnection, msgPath, - ) - if err != nil { - return err - } - return nil -} diff --git a/tests/interchain/endpoints_test.go b/tests/interchain/integrator/endpoints_test.go similarity index 99% rename from tests/interchain/endpoints_test.go rename to tests/interchain/integrator/endpoints_test.go index c888cef6ddf..434253423b6 100644 --- a/tests/interchain/endpoints_test.go +++ b/tests/interchain/integrator/endpoints_test.go @@ -1,4 +1,4 @@ -package interchain_test +package integrator_test import ( "encoding/json" diff --git a/tests/interchain/permissionless_test.go b/tests/interchain/permissionless_test.go deleted file mode 100644 index 18ed527cab6..00000000000 --- a/tests/interchain/permissionless_test.go +++ /dev/null @@ -1,909 +0,0 @@ -package interchain_test - -import ( - "context" - "encoding/json" - "fmt" - "path" - "path/filepath" - "strconv" - "strings" - "testing" - "time" - - sdkmath "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" - transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" - ccvclient "github.com/cosmos/interchain-security/v5/x/ccv/provider/client" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - "github.com/strangelove-ventures/interchaintest/v8" - "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v8/ibc" - "github.com/strangelove-ventures/interchaintest/v8/testutil" - "github.com/stretchr/testify/suite" - "github.com/tidwall/gjson" - "github.com/tidwall/sjson" - "golang.org/x/mod/semver" - "golang.org/x/sync/errgroup" -) - -const ( - permissionlessDepositPeriod = 7 * time.Minute -) - -type PermissionlessConsumersSuite struct { - *chainsuite.Suite - consumerCfg chainsuite.ConsumerConfig -} - -func (s *PermissionlessConsumersSuite) addConsumer() *chainsuite.Chain { - consumer, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, s.consumerCfg) - s.Require().NoError(err) - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) - return consumer -} - -func (s *PermissionlessConsumersSuite) isOverV19() bool { - return semver.Compare(s.Env.OldGaiaImageVersion, "v19.0.0") > 0 -} - -func (s *PermissionlessConsumersSuite) TestConsumerAdditionMigration() { - if s.isOverV19() { - s.T().Skip("Migration test for v19 -> v20") - } - consumer := s.addConsumer() - json, _, err := s.Chain.GetNode().ExecQuery(s.GetContext(), "gov", "proposals") - s.Require().NoError(err) - chainsuite.GetLogger(s.GetContext()).Sugar().Infof("%s", string(json)) - - proposals, err := s.Chain.GovQueryProposalsV1(s.GetContext(), govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED) - s.Require().NoError(err) - s.Require().Len(proposals, 1) - oldProposalCh1 := proposals[0] - - chainIDCh2 := s.consumerCfg.ChainName + "-2" - propWaiter, errCh, err := s.Chain.SubmitConsumerAdditionProposal(s.GetContext(), chainIDCh2, s.consumerCfg, time.Now().Add(permissionlessDepositPeriod+2*time.Minute)) - s.Require().NoError(err) - - s.UpgradeChain() - - proposals, err = s.Chain.GovQueryProposalsV1(s.GetContext(), govtypes.ProposalStatus_PROPOSAL_STATUS_PASSED) - s.Require().NoError(err) - s.Require().Len(proposals, 2) - newProposalCh1 := proposals[0] - s.Require().Equal(oldProposalCh1, newProposalCh1) - - proposals, err = s.Chain.GovQueryProposalsV1(s.GetContext(), govtypes.ProposalStatus_PROPOSAL_STATUS_DEPOSIT_PERIOD) - s.Require().NoError(err) - s.Require().Len(proposals, 1) - newProposalCh2 := proposals[0] - s.Require().Contains(newProposalCh2.Messages[0].TypeUrl, "MsgUpdateConsumer") - - // check that the new chain is around - chain, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("chains.#(chain_id=%q)", consumer.Config().ChainID), "provider", "list-consumer-chains") - s.Require().NoError(err) - s.Require().True(chain.Exists()) - - chain2, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("chains.#(chain_id=%q)", chainIDCh2), "provider", "list-consumer-chains") - s.Require().NoError(err) - s.Require().True(chain2.Exists()) - s.Require().Equal(uint64(0), chain2.Get("top_N").Uint()) - - propWaiter.AllowDeposit() - propWaiter.WaitForVotingPeriod() - propWaiter.AllowVote() - propWaiter.WaitForPassed() - s.Require().NoError(<-errCh) - - testutil.WaitForBlocks(s.GetContext(), 2, s.Chain) - - chain2, err = s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("chains.#(chain_id=%q)", chainIDCh2), "provider", "list-consumer-chains") - s.Require().NoError(err) - s.Require().True(chain2.Exists()) - s.Require().Equal(uint64(100), chain2.Get("top_N").Uint()) -} - -func (s *PermissionlessConsumersSuite) TestConsumerRemovalMigration() { - if s.isOverV19() { - s.T().Skip("Migration test for v19 -> v20") - } - - consumer := s.addConsumer() - - stopTime := time.Now().Add(permissionlessDepositPeriod + 2*time.Minute) - - propID := s.submitConsumerRemoval(consumer, stopTime) - - s.UpgradeChain() - - proposals, err := s.Chain.GovQueryProposalsV1(s.GetContext(), govtypes.ProposalStatus_PROPOSAL_STATUS_DEPOSIT_PERIOD) - s.Require().NoError(err) - s.Require().Len(proposals, 1) - newProposalCh2 := proposals[0] - s.Require().Contains(newProposalCh2.Messages[0].TypeUrl, "MsgRemoveConsumer") - - chain, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("chains.#(chain_id=%q)", consumer.Config().ChainID), "provider", "list-consumer-chains") - s.Require().NoError(err) - s.Require().True(chain.Exists()) - - s.depositAndPass(propID) - - chainsuite.GetLogger(s.GetContext()).Sugar().Infof("waiting for stop time %s", stopTime) - time.Sleep(time.Until(stopTime)) - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain)) - - chain, err = s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("chains.#(chain_id=%q)", consumer.Config().ChainID), "provider", "list-consumer-chains") - s.Require().NoError(err) - s.Require().True(chain.Exists()) - s.Require().Equal("CONSUMER_PHASE_STOPPED", chain.Get("phase").String()) -} - -func (s *PermissionlessConsumersSuite) TestConsumerModificationMigration() { - if s.isOverV19() { - s.T().Skip("Migration test for v19 -> v20") - } - - consumer := s.addConsumer() - - propID := s.submitConsumerModification(consumer) - - s.UpgradeChain() - - proposals, err := s.Chain.GovQueryProposalsV1(s.GetContext(), govtypes.ProposalStatus_PROPOSAL_STATUS_DEPOSIT_PERIOD) - s.Require().NoError(err) - s.Require().Len(proposals, 1) - newProposalCh2 := proposals[0] - s.Require().Contains(newProposalCh2.Messages[0].TypeUrl, "MsgUpdateConsumer") - - s.depositAndPass(propID) - - chain, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("chains.#(chain_id=%q)", consumer.Config().ChainID), "provider", "list-consumer-chains") - s.Require().NoError(err) - s.Require().True(chain.Exists()) - s.Require().Equal(uint64(80), chain.Get("top_N").Uint()) -} - -func (s *PermissionlessConsumersSuite) TestChangeRewardDenomMigration() { - if s.isOverV19() { - s.T().Skip("Migration test for v19 -> v20") - } - - consumer := s.addConsumer() - - denom, propID := s.submitChangeRewardDenoms(consumer) - - s.UpgradeChain() - - proposals, err := s.Chain.GovQueryProposalsV1(s.GetContext(), govtypes.ProposalStatus_PROPOSAL_STATUS_DEPOSIT_PERIOD) - s.Require().NoError(err) - s.Require().Len(proposals, 1) - newProposalCh2 := proposals[0] - s.Require().Contains(newProposalCh2.Messages[0].TypeUrl, "MsgChangeRewardDenoms") - - s.depositAndPass(propID) - - denoms, err := s.Chain.QueryJSON(s.GetContext(), "denoms", "provider", "registered-consumer-reward-denoms") - s.Require().NoError(err) - s.Require().Contains(denoms.String(), denom) -} - -func (s *PermissionlessConsumersSuite) depositAndPass(propID string) { - _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "gov", "deposit", propID, chainsuite.GovDepositAmount) - s.Require().NoError(err) - s.Require().NoError(s.Chain.PassProposal(s.GetContext(), propID)) - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain)) -} - -func (s *PermissionlessConsumersSuite) TestPassedProposalsDontChange() { - if s.isOverV19() { - s.T().Skip("Migration test for v19 -> v20") - } - consumer := s.addConsumer() - - _, denomPropID := s.submitChangeRewardDenoms(consumer) - s.depositAndPass(denomPropID) - - denomPropIDInt, err := strconv.Atoi(denomPropID) - s.Require().NoError(err) - denomProposal, err := s.Chain.GovQueryProposalV1(s.GetContext(), uint64(denomPropIDInt)) - s.Require().NoError(err) - - modificationPropID := s.submitConsumerModification(consumer) - s.depositAndPass(modificationPropID) - - modificationPropIDInt, err := strconv.Atoi(modificationPropID) - s.Require().NoError(err) - modificationProposal, err := s.Chain.GovQueryProposalV1(s.GetContext(), uint64(modificationPropIDInt)) - s.Require().NoError(err) - - stopTime := time.Now().Add(permissionlessDepositPeriod + 2*time.Minute) - removalPropID := s.submitConsumerRemoval(consumer, stopTime) - s.depositAndPass(removalPropID) - - removalPropIDInt, err := strconv.Atoi(removalPropID) - s.Require().NoError(err) - removalProposal, err := s.Chain.GovQueryProposalV1(s.GetContext(), uint64(removalPropIDInt)) - s.Require().NoError(err) - - s.UpgradeChain() - - denomProposalAfter, err := s.Chain.GovQueryProposalV1(s.GetContext(), uint64(denomPropIDInt)) - s.Require().NoError(err) - s.Require().Equal(denomProposal, denomProposalAfter) - - modificationProposalAfter, err := s.Chain.GovQueryProposalV1(s.GetContext(), uint64(modificationPropIDInt)) - s.Require().NoError(err) - s.Require().Equal(modificationProposal, modificationProposalAfter) - - removalProposalAfter, err := s.Chain.GovQueryProposalV1(s.GetContext(), uint64(removalPropIDInt)) - s.Require().NoError(err) - s.Require().Equal(removalProposal, removalProposalAfter) - - time.Sleep(time.Until(stopTime)) - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain)) - - chain, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("chains.#(chain_id=%q)", consumer.Config().ChainID), "provider", "list-consumer-chains") - s.Require().NoError(err) - s.Require().True(chain.Exists()) - s.Require().Equal("CONSUMER_PHASE_STOPPED", chain.Get("phase").String()) -} - -func (s *PermissionlessConsumersSuite) TestChangeOwner() { - s.UpgradeChain() - - cfg := s.consumerCfg - cfg.TopN = 0 - cfg.BeforeSpawnTime = func(ctx context.Context, consumer *cosmos.CosmosChain) { - consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) - s.Require().NoError(err) - eg := errgroup.Group{} - for i := 0; i < 3; i++ { - i := i - eg.Go(func() error { - _, err := s.Chain.Validators[i].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[i].Moniker, "provider", "opt-in", consumerID) - return err - }) - } - s.Require().NoError(eg.Wait()) - } - consumer, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, cfg) - s.Require().NoError(err) - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) - - govAddress, err := s.Chain.GetGovernanceAddress(s.GetContext()) - s.Require().NoError(err) - consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) - s.Require().NoError(err) - update := &providertypes.MsgUpdateConsumer{ - ConsumerId: consumerID, - NewOwnerAddress: govAddress, - Metadata: &providertypes.ConsumerMetadata{ - Name: consumer.Config().Name, - Description: "Consumer chain", - Metadata: "ipfs://", - }, - } - updateBz, err := json.Marshal(update) - s.Require().NoError(err) - err = s.Chain.GetNode().WriteFile(s.GetContext(), updateBz, "consumer-update.json") - s.Require().NoError(err) - _, err = s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, - "provider", "update-consumer", path.Join(s.Chain.GetNode().HomeDir(), "consumer-update.json")) - s.Require().NoError(err) - - update.Owner = govAddress - update.NewOwnerAddress = s.Chain.ValidatorWallets[0].Address - prop, err := s.Chain.BuildProposal([]cosmos.ProtoMessage{update}, - "update consumer", "update consumer", "", - chainsuite.GovDepositAmount, "", false) - s.Require().NoError(err) - txhash, err := s.Chain.GetNode().SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop) - s.Require().NoError(err) - propID, err := s.Chain.GetProposalID(s.GetContext(), txhash) - s.Require().NoError(err) - s.Require().NoError(s.Chain.PassProposal(s.GetContext(), propID)) -} - -func (s *PermissionlessConsumersSuite) TestChangePowerShaping() { - s.UpgradeChain() - - cfg := s.consumerCfg - cfg.TopN = 0 - const ( - oldValidatorCount = 4 - newValidatorCount = 3 - ) - cfg.BeforeSpawnTime = func(ctx context.Context, consumer *cosmos.CosmosChain) { - consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) - s.Require().NoError(err) - eg := errgroup.Group{} - for i := 0; i < oldValidatorCount; i++ { - i := i - eg.Go(func() error { - _, err := s.Chain.Validators[i].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[i].Moniker, "provider", "opt-in", consumerID) - return err - }) - } - s.Require().NoError(eg.Wait()) - } - consumer, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, cfg) - s.Require().NoError(err) - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) - - consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) - s.Require().NoError(err) - update := &providertypes.MsgUpdateConsumer{ - ConsumerId: consumerID, - Metadata: &providertypes.ConsumerMetadata{ - Name: consumer.Config().Name, - Description: "Consumer chain", - Metadata: "ipfs://", - }, - PowerShapingParameters: &providertypes.PowerShapingParameters{ - ValidatorSetCap: newValidatorCount, - }, - } - updateBz, err := json.Marshal(update) - s.Require().NoError(err) - err = s.Chain.GetNode().WriteFile(s.GetContext(), updateBz, "consumer-update.json") - s.Require().NoError(err) - _, err = s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, - "provider", "update-consumer", path.Join(s.Chain.GetNode().HomeDir(), "consumer-update.json")) - s.Require().NoError(err) - - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) - - vals, err := consumer.QueryJSON(s.GetContext(), "validators", "tendermint-validator-set") - s.Require().NoError(err) - s.Require().Equal(newValidatorCount, len(vals.Array()), vals) - for i := 0; i < newValidatorCount; i++ { - valCons := vals.Array()[i].Get("address").String() - s.Require().NoError(err) - s.Require().Equal(consumer.ValidatorWallets[i].ValConsAddress, valCons) - } -} - -func (s *PermissionlessConsumersSuite) TestConsumerCommissionRate() { - s.UpgradeChain() - cfg := s.consumerCfg - - cfg.TopN = 0 - cfg.BeforeSpawnTime = func(ctx context.Context, consumer *cosmos.CosmosChain) { - consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) - s.Require().NoError(err) - _, err = s.Chain.Validators[0].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "opt-in", consumerID) - s.Require().NoError(err) - } - - images := []ibc.DockerImage{ - { - Repository: "ghcr.io/hyphacoop/ics", - Version: "v4.5.0", - UidGid: "1025:1025", - }, - } - chainID := fmt.Sprintf("%s-test-%d", cfg.ChainName, len(s.Chain.Consumers)+1) - spawnTime := time.Now().Add(chainsuite.ChainSpawnWait) - cfg.Spec = s.Chain.DefaultConsumerChainSpec(s.GetContext(), chainID, cfg, spawnTime, nil) - cfg.Spec.Version = "v4.5.0" - cfg.Spec.Images = images - consumer1, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, cfg) - s.Require().NoError(err) - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer1, s.Relayer, 1_000_000, 0, 1)) - - chainID = fmt.Sprintf("%s-test-%d", cfg.ChainName, len(s.Chain.Consumers)+1) - spawnTime = time.Now().Add(chainsuite.ChainSpawnWait) - cfg.Spec = s.Chain.DefaultConsumerChainSpec(s.GetContext(), chainID, cfg, spawnTime, nil) - cfg.Spec.Version = "v4.5.0" - cfg.Spec.Images = images - consumer2, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, cfg) - s.Require().NoError(err) - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer2, s.Relayer, 1_000_000, 0, 1)) - - for i := 1; i < chainsuite.ValidatorCount; i++ { - s.Require().NoError(consumer1.Validators[i].StopContainer(s.GetContext())) - s.Require().NoError(consumer2.Validators[i].StopContainer(s.GetContext())) - } - - consumer1Ch, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, consumer1) - s.Require().NoError(err) - consumer2Ch, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, consumer2) - s.Require().NoError(err) - denom1 := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", consumer1Ch.ChannelID, consumer1.Config().Denom)).IBCDenom() - denom2 := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", consumer2Ch.ChannelID, consumer2.Config().Denom)).IBCDenom() - - s.Require().NotEqual(denom1, denom2, "denom1: %s, denom2: %s; channel1: %s, channel2: %s", denom1, denom2, consumer1Ch.Counterparty.ChannelID, consumer2Ch.Counterparty.ChannelID) - - govAuthority, err := s.Chain.GetGovernanceAddress(s.GetContext()) - s.Require().NoError(err) - rewardDenomsProp := providertypes.MsgChangeRewardDenoms{ - DenomsToAdd: []string{denom1, denom2}, - Authority: govAuthority, - } - prop, err := s.Chain.BuildProposal([]cosmos.ProtoMessage{&rewardDenomsProp}, - "add denoms to list of registered reward denoms", - "add denoms to list of registered reward denoms", - "", chainsuite.GovDepositAmount, "", false) - s.Require().NoError(err) - propResult, err := s.Chain.SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop) - s.Require().NoError(err) - s.Require().NoError(s.Chain.PassProposal(s.GetContext(), propResult.ProposalID)) - - eg := errgroup.Group{} - - _, err = s.Chain.Validators[0].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-all-rewards") - s.Require().NoError(err) - - _, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, "--commission") - s.Require().NoError(err) - - consumerID1, err := s.Chain.GetConsumerID(s.GetContext(), consumer1.Config().ChainID) - s.Require().NoError(err) - consumerID2, err := s.Chain.GetConsumerID(s.GetContext(), consumer2.Config().ChainID) - s.Require().NoError(err) - - eg.Go(func() error { - _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "set-consumer-commission-rate", consumerID1, "0.5") - return err - }) - eg.Go(func() error { - _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "set-consumer-commission-rate", consumerID2, "0.5") - return err - }) - s.Require().NoError(eg.Wait()) - - _, err = s.Chain.Validators[0].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, "--commission") - s.Require().NoError(err) - - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 1, consumer1, consumer2)) - - eg.Go(func() error { - _, err := consumer1.Validators[0].ExecTx(s.GetContext(), consumer1.ValidatorWallets[0].Moniker, "bank", "send", consumer1.ValidatorWallets[0].Address, consumer1.ValidatorWallets[1].Address, "1"+consumer1.Config().Denom, "--fees", "100000000"+consumer1.Config().Denom) - return err - }) - eg.Go(func() error { - _, err := consumer2.Validators[0].ExecTx(s.GetContext(), consumer2.ValidatorWallets[0].Moniker, "bank", "send", consumer2.ValidatorWallets[0].Address, consumer2.ValidatorWallets[1].Address, "1"+consumer2.Config().Denom, "--fees", "100000000"+consumer2.Config().Denom) - return err - }) - s.Require().NoError(eg.Wait()) - - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), chainsuite.BlocksPerDistribution+2, s.Chain, consumer1, consumer2)) - s.Require().NoError(s.Relayer.ClearTransferChannel(s.GetContext(), s.Chain, consumer1)) - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain, consumer1, consumer2)) - - rewardStr, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", denom1), "distribution", "rewards", s.Chain.ValidatorWallets[0].Address) - s.Require().NoError(err) - rewardsDenom1, err := chainsuite.StrToSDKInt(rewardStr.String()) - s.Require().NoError(err) - rewardStr, err = s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", denom2), "distribution", "rewards", s.Chain.ValidatorWallets[0].Address) - s.Require().NoError(err) - rewardsDenom2, err := chainsuite.StrToSDKInt(rewardStr.String()) - s.Require().NoError(err) - - s.Require().NotEmpty(rewardsDenom1) - s.Require().NotEmpty(rewardsDenom2) - s.Require().True(rewardsDenom1.Sub(rewardsDenom2).Abs().LT(sdkmath.NewInt(1000)), "rewards1Int: %s, rewards2Int: %s", rewardsDenom1.String(), rewardsDenom2.String()) - - _, err = s.Chain.Validators[0].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, "--commission") - s.Require().NoError(err) - - eg.Go(func() error { - _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "set-consumer-commission-rate", consumerID1, "0.25") - return err - }) - eg.Go(func() error { - _, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "provider", "set-consumer-commission-rate", consumerID2, "0.5") - return err - }) - s.Require().NoError(eg.Wait()) - - _, err = s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, "--commission") - s.Require().NoError(err) - - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 1, consumer1, consumer2)) - - eg.Go(func() error { - _, err := consumer1.Validators[0].ExecTx(s.GetContext(), consumer1.ValidatorWallets[0].Moniker, "bank", "send", consumer1.ValidatorWallets[0].Address, consumer1.ValidatorWallets[1].Address, "1"+consumer1.Config().Denom, "--fees", "100000000"+consumer1.Config().Denom) - return err - }) - eg.Go(func() error { - _, err := consumer2.Validators[0].ExecTx(s.GetContext(), consumer2.ValidatorWallets[0].Moniker, "bank", "send", consumer2.ValidatorWallets[0].Address, consumer2.ValidatorWallets[1].Address, "1"+consumer2.Config().Denom, "--fees", "100000000"+consumer2.Config().Denom) - return err - }) - s.Require().NoError(eg.Wait()) - - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), chainsuite.BlocksPerDistribution+2, s.Chain, consumer1, consumer2)) - s.Require().NoError(s.Relayer.ClearTransferChannel(s.GetContext(), s.Chain, consumer1)) - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain, consumer1, consumer2)) - - rewardStr, err = s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", denom1), "distribution", "rewards", s.Chain.ValidatorWallets[0].Address) - s.Require().NoError(err) - rewardsDenom1, err = chainsuite.StrToSDKInt(rewardStr.String()) - s.Require().NoError(err) - rewardStr, err = s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", denom2), "distribution", "rewards", s.Chain.ValidatorWallets[0].Address) - s.Require().NoError(err) - rewardsDenom2, err = chainsuite.StrToSDKInt(rewardStr.String()) - s.Require().NoError(err) - - s.Require().True(rewardsDenom1.GT(rewardsDenom2), "rewards1Int: %s, rewards2Int: %s", rewardsDenom1.String(), rewardsDenom2.String()) - s.Require().False(rewardsDenom1.Sub(rewardsDenom2).Abs().LT(sdkmath.NewInt(1000)), "rewards1Int: %s, rewards2Int: %s", rewardsDenom1.String(), rewardsDenom2.String()) -} - -func (s *PermissionlessConsumersSuite) TestLaunchWithAllowListThenModify() { - s.UpgradeChain() - - consumerConfig := s.consumerCfg - consumerConfig.Allowlist = []string{ - s.Chain.ValidatorWallets[0].ValConsAddress, - s.Chain.ValidatorWallets[1].ValConsAddress, - s.Chain.ValidatorWallets[2].ValConsAddress, - } - consumerConfig.TopN = 0 - consumerConfig.BeforeSpawnTime = func(ctx context.Context, consumer *cosmos.CosmosChain) { - consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) - s.Require().NoError(err) - eg := errgroup.Group{} - for i := 0; i < 3; i++ { - i := i - eg.Go(func() error { - _, err := s.Chain.Validators[i].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[i].Moniker, "provider", "opt-in", consumerID) - return err - }) - } - s.Require().NoError(eg.Wait()) - } - - consumer, err := s.Chain.AddConsumerChain(s.GetContext(), s.Relayer, consumerConfig) - s.Require().NoError(err) - - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) - - consumerID, err := s.Chain.GetConsumerID(s.GetContext(), consumer.Config().ChainID) - s.Require().NoError(err) - - // ensure we can't opt in a non-allowlisted validator - _, err = s.Chain.Validators[3].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[3].Moniker, - "provider", "opt-in", consumerID) - s.Require().NoError(err) - - validators, err := consumer.QueryJSON(s.GetContext(), "validators", "tendermint-validator-set") - s.Require().NoError(err) - s.Require().Equal(3, len(validators.Array())) - - update := &providertypes.MsgUpdateConsumer{ - ConsumerId: consumerID, - PowerShapingParameters: &providertypes.PowerShapingParameters{ - Allowlist: []string{}, - }, - } - updateBz, err := json.Marshal(update) - s.Require().NoError(err) - err = s.Chain.GetNode().WriteFile(s.GetContext(), updateBz, "consumer-update.json") - s.Require().NoError(err) - _, err = s.Chain.GetNode().ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, - "provider", "update-consumer", path.Join(s.Chain.GetNode().HomeDir(), "consumer-update.json")) - s.Require().NoError(err) - - // // ensure we can opt in a non-allowlisted validator after the modification - _, err = s.Chain.Validators[3].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[3].Moniker, - "provider", "opt-in", consumerID) - s.Require().NoError(err) - validators, err = consumer.QueryJSON(s.GetContext(), "validators", "tendermint-validator-set") - s.Require().NoError(err) - s.Require().Equal(4, len(validators.Array())) -} - -func (s *PermissionlessConsumersSuite) TestRewardsWithChangeover() { - validators := 1 - fullNodes := 0 - genesisChanges := []cosmos.GenesisKV{ - cosmos.NewGenesisKV("app_state.gov.params.voting_period", chainsuite.GovVotingPeriod.String()), - cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", chainsuite.GovDepositPeriod.String()), - cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.denom", chainsuite.Ucon), - cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.amount", strconv.Itoa(chainsuite.GovMinDepositAmount)), - } - spec := &interchaintest.ChainSpec{ - Name: "ics-consumer", - ChainName: "ics-consumer", - // Unfortunately, this rc is a bit of a bespoke version; it corresponds to an rc - // in hypha's fork that has a fix for sovereign -> consumer changeovers - Version: "v6.2.0-rc1", - NumValidators: &validators, - NumFullNodes: &fullNodes, - ChainConfig: ibc.ChainConfig{ - Denom: chainsuite.Ucon, - GasPrices: "0.025" + chainsuite.Ucon, - GasAdjustment: 2.0, - Gas: "auto", - ConfigFileOverrides: map[string]any{ - "config/config.toml": chainsuite.DefaultConfigToml(), - }, - ModifyGenesisAmounts: chainsuite.DefaultGenesisAmounts(chainsuite.Ucon), - ModifyGenesis: cosmos.ModifyGenesis(genesisChanges), - Bin: "interchain-security-sd", - Images: []ibc.DockerImage{ - { - Repository: chainsuite.HyphaICSRepo, - Version: "v6.2.0-rc1", - UidGid: chainsuite.ICSUidGuid, - }, - }, - Bech32Prefix: "consumer", - }, - } - consumer, err := s.Chain.AddLinkedChain(s.GetContext(), s.T(), s.Relayer, spec) - s.Require().NoError(err) - - transferCh, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, consumer) - s.Require().NoError(err) - rewardDenom := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", transferCh.ChannelID, consumer.Config().Denom)).IBCDenom() - - s.UpgradeChain() - - s.changeSovereignToConsumer(consumer, transferCh) - - govAuthority, err := s.Chain.GetGovernanceAddress(s.GetContext()) - s.Require().NoError(err) - rewardDenomsProp := providertypes.MsgChangeRewardDenoms{ - DenomsToAdd: []string{rewardDenom}, - Authority: govAuthority, - } - prop, err := s.Chain.BuildProposal([]cosmos.ProtoMessage{&rewardDenomsProp}, - "add denoms to list of registered reward denoms", - "add denoms to list of registered reward denoms", - "", chainsuite.GovDepositAmount, "", false) - s.Require().NoError(err) - propResult, err := s.Chain.SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop) - s.Require().NoError(err) - s.Require().NoError(s.Chain.PassProposal(s.GetContext(), propResult.ProposalID)) - - faucetAddrBts, err := consumer.GetAddress(s.GetContext(), interchaintest.FaucetAccountKeyName) - s.Require().NoError(err) - faucetAddr := types.MustBech32ifyAddressBytes(consumer.Config().Bech32Prefix, faucetAddrBts) - _, err = consumer.Validators[0].ExecTx(s.GetContext(), interchaintest.FaucetAccountKeyName, "bank", "send", string(faucetAddr), consumer.ValidatorWallets[0].Address, "1"+consumer.Config().Denom, "--fees", "100000000"+consumer.Config().Denom) - s.Require().NoError(err) - - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), chainsuite.BlocksPerDistribution+2, s.Chain, consumer)) - s.Require().NoError(s.Relayer.ClearTransferChannel(s.GetContext(), s.Chain, consumer)) - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain, consumer)) - - rewardStr, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", rewardDenom), "distribution", "rewards", s.Chain.ValidatorWallets[0].Address) - s.Require().NoError(err) - rewards, err := chainsuite.StrToSDKInt(rewardStr.String()) - s.Require().NoError(err) - s.Require().True(rewards.GT(sdkmath.NewInt(0)), "rewards: %s", rewards.String()) -} - -func TestPermissionlessConsumers(t *testing.T) { - genesis := chainsuite.DefaultGenesis() - genesis = append(genesis, - cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", permissionlessDepositPeriod.String()), - ) - s := &PermissionlessConsumersSuite{ - Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ - CreateRelayer: true, - Scope: chainsuite.ChainScopeTest, - ChainSpec: &interchaintest.ChainSpec{ - ChainConfig: ibc.ChainConfig{ - ModifyGenesis: cosmos.ModifyGenesis(genesis), - }, - }, - }), - consumerCfg: chainsuite.ConsumerConfig{ - ChainName: "ics-consumer", - Version: "v4.5.0", - ShouldCopyProviderKey: allProviderKeysCopied(), - Denom: chainsuite.Ucon, - TopN: 100, - AllowInactiveVals: true, - MinStake: 1_000_000, - Spec: &interchaintest.ChainSpec{ - ChainConfig: ibc.ChainConfig{ - Images: []ibc.DockerImage{ - { - Repository: chainsuite.HyphaICSRepo, - Version: "v4.5.0", - UidGid: chainsuite.ICSUidGuid, - }, - }, - }, - }, - }, - } - suite.Run(t, s) -} - -func (s *PermissionlessConsumersSuite) changeSovereignToConsumer(consumer *chainsuite.Chain, transferCh *ibc.ChannelOutput) { - cfg := s.consumerCfg - cfg.TopN = 0 - currentHeight, err := consumer.Height(s.GetContext()) - s.Require().NoError(err) - initialHeight := uint64(currentHeight) + 60 - cfg.InitialHeight = initialHeight - spawnTime := time.Now().Add(60 * time.Second) - cfg.DistributionTransmissionChannel = transferCh.ChannelID - - err = s.Chain.CreateConsumerPermissionless(s.GetContext(), consumer.Config().ChainID, cfg, spawnTime) - s.Require().NoError(err) - - consumerChains, _, err := s.Chain.GetNode().ExecQuery(s.GetContext(), "provider", "list-consumer-chains") - s.Require().NoError(err) - consumerChain := gjson.GetBytes(consumerChains, fmt.Sprintf("chains.#(chain_id=%q)", consumer.Config().ChainID)) - consumerID := consumerChain.Get("consumer_id").String() - - eg := errgroup.Group{} - for i := range consumer.Validators { - i := i - eg.Go(func() error { - key, _, err := consumer.Validators[i].ExecBin(s.GetContext(), "tendermint", "show-validator") - if err != nil { - return err - } - keyStr := strings.TrimSpace(string(key)) - _, err = s.Chain.Validators[i].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[i].Moniker, "provider", "opt-in", consumerID, keyStr) - return err - }) - } - s.Require().NoError(eg.Wait()) - - s.Require().NoError(err) - time.Sleep(time.Until(spawnTime)) - s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Chain)) - - proposal := cosmos.SoftwareUpgradeProposal{ - Deposit: "5000000" + chainsuite.Ucon, - Title: "Changeover", - Name: "sovereign-changeover", - Description: "Changeover", - Height: int64(initialHeight) - 3, - } - upgradeTx, err := consumer.UpgradeProposal(s.GetContext(), interchaintest.FaucetAccountKeyName, proposal) - s.Require().NoError(err) - err = consumer.PassProposal(s.GetContext(), upgradeTx.ProposalID) - s.Require().NoError(err) - - currentHeight, err = consumer.Height(s.GetContext()) - s.Require().NoError(err) - - timeoutCtx, timeoutCtxCancel := context.WithTimeout(s.GetContext(), (time.Duration(int64(initialHeight)-currentHeight)+10)*chainsuite.CommitTimeout) - defer timeoutCtxCancel() - err = testutil.WaitForBlocks(timeoutCtx, int(int64(initialHeight)-currentHeight)+3, consumer) - s.Require().Error(err) - - s.Require().NoError(consumer.StopAllNodes(s.GetContext())) - - genesis, err := consumer.GetNode().GenesisFileContent(s.GetContext()) - s.Require().NoError(err) - - ccvState, _, err := s.Chain.GetNode().ExecQuery(s.GetContext(), "provider", "consumer-genesis", consumerID) - s.Require().NoError(err) - genesis, err = sjson.SetRawBytes(genesis, "app_state.ccvconsumer", ccvState) - s.Require().NoError(err) - - genesis, err = sjson.SetBytes(genesis, "app_state.slashing.params.signed_blocks_window", strconv.Itoa(chainsuite.SlashingWindowConsumer)) - s.Require().NoError(err) - genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.params.reward_denoms", []string{chainsuite.Ucon}) - s.Require().NoError(err) - genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.params.provider_reward_denoms", []string{s.Chain.Config().Denom}) - s.Require().NoError(err) - genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.params.blocks_per_distribution_transmission", chainsuite.BlocksPerDistribution) - s.Require().NoError(err) - - for _, val := range consumer.Validators { - val := val - eg.Go(func() error { - if err := val.OverwriteGenesisFile(s.GetContext(), []byte(genesis)); err != nil { - return err - } - return val.WriteFile(s.GetContext(), []byte(genesis), ".sovereign/config/genesis.json") - }) - } - s.Require().NoError(eg.Wait()) - - consumer.ChangeBinary(s.GetContext(), "interchain-security-cdd") - s.Require().NoError(consumer.StartAllNodes(s.GetContext())) - s.Require().NoError(s.Relayer.ConnectProviderConsumer(s.GetContext(), s.Chain, consumer)) - s.Require().NoError(s.Relayer.StopRelayer(s.GetContext(), chainsuite.GetRelayerExecReporter(s.GetContext()))) - s.Require().NoError(s.Relayer.StartRelayer(s.GetContext(), chainsuite.GetRelayerExecReporter(s.GetContext()))) - s.Require().NoError(s.Chain.CheckCCV(s.GetContext(), consumer, s.Relayer, 1_000_000, 0, 1)) -} - -func (s *PermissionlessConsumersSuite) submitChangeRewardDenoms(consumer *chainsuite.Chain) (string, string) { - consumerCh, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Chain, consumer) - s.Require().NoError(err) - denom := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", consumerCh.ChannelID, consumer.Config().Denom)).IBCDenom() - - denomProp := &ccvclient.ChangeRewardDenomsProposalJSON{ - ChangeRewardDenomsProposal: providertypes.ChangeRewardDenomsProposal{ - Title: "change reward denoms", - Description: "change reward denoms", - DenomsToAdd: []string{denom}, - DenomsToRemove: []string{}, - }, - Deposit: fmt.Sprintf("%d%s", chainsuite.GovMinDepositAmount/2, s.Chain.Config().Denom), - Summary: "change reward denoms", - } - propBz, err := json.Marshal(denomProp) - s.Require().NoError(err) - - fileName := "proposal_consumer_denoms.json" - - s.Require().NoError(s.Chain.GetNode().WriteFile(s.GetContext(), propBz, fileName)) - - filePath := filepath.Join(s.Chain.GetNode().HomeDir(), fileName) - - txhash, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, - "gov", "submit-legacy-proposal", "change-reward-denoms", filePath, - "--gas", "auto", - ) - s.Require().NoError(err) - - propID, err := s.Chain.GetProposalID(s.GetContext(), txhash) - s.Require().NoError(err) - return denom, propID -} - -func (s *PermissionlessConsumersSuite) submitConsumerModification(consumer *chainsuite.Chain) string { - modifyProp := &ccvclient.ConsumerModificationProposalJSON{ - Title: "modify consumer", - Summary: "modify consumer", - ChainId: consumer.Config().ChainID, - TopN: 80, - Deposit: fmt.Sprintf("%d%s", chainsuite.GovMinDepositAmount/2, s.Chain.Config().Denom), - } - - propBz, err := json.Marshal(modifyProp) - s.Require().NoError(err) - - propBz, err = sjson.DeleteBytes(propBz, "allow_inactive_vals") - s.Require().NoError(err) - propBz, err = sjson.DeleteBytes(propBz, "min_stake") - s.Require().NoError(err) - - fileName := "proposal_consumer_modification.json" - - s.Require().NoError(s.Chain.GetNode().WriteFile(s.GetContext(), propBz, fileName)) - - filePath := filepath.Join(s.Chain.GetNode().HomeDir(), fileName) - - txhash, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, - "gov", "submit-legacy-proposal", "consumer-modification", filePath, - "--gas", "auto", - ) - s.Require().NoError(err) - - propID, err := s.Chain.GetProposalID(s.GetContext(), txhash) - s.Require().NoError(err) - return propID -} - -func (s *PermissionlessConsumersSuite) submitConsumerRemoval(consumer *chainsuite.Chain, stopTime time.Time) string { - removalProp := &ccvclient.ConsumerRemovalProposalJSON{ - Title: "remove consumer", - Summary: "remove consumer", - ChainId: consumer.Config().ChainID, - StopTime: stopTime, - Deposit: fmt.Sprintf("%d%s", chainsuite.GovMinDepositAmount/2, s.Chain.Config().Denom), - } - - propBz, err := json.Marshal(removalProp) - s.Require().NoError(err) - - fileName := "proposal_consumer_removal.json" - - s.Require().NoError(s.Chain.GetNode().WriteFile(s.GetContext(), propBz, fileName)) - - filePath := filepath.Join(s.Chain.GetNode().HomeDir(), fileName) - - txhash, err := s.Chain.GetNode().ExecTx(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, - "gov", "submit-legacy-proposal", "consumer-removal", filePath, - "--gas", "auto", - ) - s.Require().NoError(err) - - propID, err := s.Chain.GetProposalID(s.GetContext(), txhash) - s.Require().NoError(err) - return propID -} diff --git a/tests/interchain/txs_test.go b/tests/interchain/txs_test.go deleted file mode 100644 index 85d2b40df27..00000000000 --- a/tests/interchain/txs_test.go +++ /dev/null @@ -1,486 +0,0 @@ -package interchain_test - -import ( - "context" - "fmt" - "path" - "testing" - "time" - - "github.com/strangelove-ventures/interchaintest/v8" - "github.com/strangelove-ventures/interchaintest/v8/ibc" - "github.com/strangelove-ventures/interchaintest/v8/testutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - - sdkmath "cosmossdk.io/math" - - "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" - - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -const txAmount = 1_000_000_000 - -type TxSuite struct { - *chainsuite.Suite -} - -func txAmountUatom() string { - return fmt.Sprintf("%d%s", txAmount, chainsuite.Uatom) -} - -func (s *TxSuite) TestBankSend() { - balanceBefore, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[1].Address, chainsuite.Uatom) - s.Require().NoError(err) - - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "bank", "send", - s.Chain.ValidatorWallets[0].Address, s.Chain.ValidatorWallets[1].Address, txAmountUatom(), - ) - s.Require().NoError(err) - - balanceAfter, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[1].Address, chainsuite.Uatom) - s.Require().NoError(err) - s.Require().Equal(balanceBefore.Add(sdkmath.NewInt(txAmount)), balanceAfter) -} - -func (s TxSuite) TestDelegateWithdrawUnbond() { - // delegate tokens - _, err := s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "staking", "delegate", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), - ) - s.Require().NoError(err) - - startingBalance, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[0].Address, chainsuite.Uatom) - s.Require().NoError(err) - time.Sleep(20 * time.Second) - // Withdraw rewards - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "distribution", "withdraw-rewards", s.Chain.ValidatorWallets[0].ValoperAddress, - ) - s.Require().NoError(err) - endingBalance, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[0].Address, chainsuite.Uatom) - s.Require().NoError(err) - s.Require().Truef(endingBalance.GT(startingBalance), "endingBalance: %s, startingBalance: %s", endingBalance, startingBalance) - - // Unbond tokens - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "staking", "unbond", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), - ) - s.Require().NoError(err) -} - -func (s TxSuite) TestAuthz() { - s.Run("send", func() { - balanceBefore, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[2].Address, chainsuite.Uatom) - s.Require().NoError(err) - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "authz", "grant", s.Chain.ValidatorWallets[1].Address, "send", - "--spend-limit", fmt.Sprintf("%d%s", txAmount*2, chainsuite.Uatom), - "--allow-list", s.Chain.ValidatorWallets[2].Address, - ) - s.Require().NoError(err) - - s.Require().Error(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "bank", "send", s.Chain.ValidatorWallets[0].Address, s.Chain.ValidatorWallets[3].Address, txAmountUatom())) - - s.Require().NoError(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "bank", "send", s.Chain.ValidatorWallets[0].Address, s.Chain.ValidatorWallets[2].Address, txAmountUatom())) - balanceAfter, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[2].Address, chainsuite.Uatom) - s.Require().NoError(err) - s.Require().Equal(balanceBefore.Add(sdkmath.NewInt(int64(txAmount))), balanceAfter) - - s.Require().Error(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "bank", "send", s.Chain.ValidatorWallets[0].Address, s.Chain.ValidatorWallets[2].Address, fmt.Sprintf("%d%s", txAmount+200, chainsuite.Uatom))) - - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "authz", "revoke", s.Chain.ValidatorWallets[1].Address, "/cosmos.bank.v1beta1.MsgSend", - ) - s.Require().NoError(err) - - s.Require().Error(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "bank", "send", s.Chain.ValidatorWallets[0].Address, s.Chain.ValidatorWallets[2].Address, txAmountUatom())) - }) - - s.Run("delegate", func() { - _, err := s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "authz", "grant", s.Chain.ValidatorWallets[1].Address, "delegate", - "--allowed-validators", s.Chain.ValidatorWallets[2].ValoperAddress, - ) - s.Require().NoError(err) - - s.Require().NoError(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "staking", "delegate", s.Chain.ValidatorWallets[2].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) - - s.Require().Error(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "staking", "delegate", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) - - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "authz", "revoke", s.Chain.ValidatorWallets[1].Address, "/cosmos.staking.v1beta1.MsgDelegate", - ) - s.Require().NoError(err) - s.Require().Error(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "staking", "delegate", s.Chain.ValidatorWallets[2].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) - }) - - s.Run("unbond", func() { - valHex, err := s.Chain.GetValidatorHex(s.GetContext(), 2) - s.Require().NoError(err) - powerBefore, err := s.Chain.GetValidatorPower(s.GetContext(), valHex) - s.Require().NoError(err) - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "staking", "delegate", s.Chain.ValidatorWallets[2].ValoperAddress, txAmountUatom(), - ) - s.Require().NoError(err) - s.Require().EventuallyWithT(func(c *assert.CollectT) { - powerAfter, err := s.Chain.GetValidatorPower(s.GetContext(), valHex) - s.Require().NoError(err) - assert.NoError(c, err) - assert.Greater(c, powerAfter, powerBefore) - }, 15*chainsuite.CommitTimeout, chainsuite.CommitTimeout) - - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "authz", "grant", s.Chain.ValidatorWallets[1].Address, "unbond", - "--allowed-validators", s.Chain.ValidatorWallets[2].ValoperAddress, - ) - s.Require().NoError(err) - - s.Require().NoError(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "staking", "unbond", s.Chain.ValidatorWallets[2].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) - s.Require().Error(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "staking", "unbond", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) - - s.Require().EventuallyWithT(func(c *assert.CollectT) { - powerAfter, err := s.Chain.GetValidatorPower(s.GetContext(), valHex) - s.Require().NoError(err) - assert.NoError(c, err) - assert.Equal(c, powerAfter, powerBefore) - }, 15*chainsuite.CommitTimeout, chainsuite.CommitTimeout) - - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "authz", "revoke", s.Chain.ValidatorWallets[1].Address, "/cosmos.staking.v1beta1.MsgUndelegate", - ) - s.Require().NoError(err) - s.Require().Error(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "staking", "unbond", s.Chain.ValidatorWallets[2].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) - }) - - s.Run("redelegate", func() { - val0Hex, err := s.Chain.GetValidatorHex(s.GetContext(), 0) - s.Require().NoError(err) - val2Hex, err := s.Chain.GetValidatorHex(s.GetContext(), 2) - s.Require().NoError(err) - val0PowerBefore, err := s.Chain.GetValidatorPower(s.GetContext(), val0Hex) - s.Require().NoError(err) - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "staking", "delegate", s.Chain.ValidatorWallets[0].ValoperAddress, txAmountUatom(), - ) - s.Require().NoError(err) - s.Require().EventuallyWithT(func(c *assert.CollectT) { - val0PowerAfter, err := s.Chain.GetValidatorPower(s.GetContext(), val0Hex) - s.Require().NoError(err) - s.Require().NoError(err) - s.Require().Greater(val0PowerAfter, val0PowerBefore) - }, 15*chainsuite.CommitTimeout, chainsuite.CommitTimeout) - - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "authz", "grant", s.Chain.ValidatorWallets[1].Address, "redelegate", - "--allowed-validators", s.Chain.ValidatorWallets[2].ValoperAddress, - ) - s.Require().NoError(err) - - s.Require().Error(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "staking", "redelegate", s.Chain.ValidatorWallets[0].ValoperAddress, s.Chain.ValidatorWallets[1].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) - - val2PowerBefore, err := s.Chain.GetValidatorPower(s.GetContext(), val2Hex) - s.Require().NoError(err) - s.Require().NoError(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "staking", "redelegate", s.Chain.ValidatorWallets[0].ValoperAddress, s.Chain.ValidatorWallets[2].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) - s.Require().EventuallyWithT(func(c *assert.CollectT) { - val2PowerAfter, err := s.Chain.GetValidatorPower(s.GetContext(), val2Hex) - s.Require().NoError(err) - s.Require().Greater(val2PowerAfter, val2PowerBefore) - }, 15*chainsuite.CommitTimeout, chainsuite.CommitTimeout) - - _, err = s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "authz", "revoke", s.Chain.ValidatorWallets[1].Address, "/cosmos.staking.v1beta1.MsgBeginRedelegate", - ) - s.Require().NoError(err) - - s.Require().Error(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "staking", "redelegate", s.Chain.ValidatorWallets[0].ValoperAddress, s.Chain.ValidatorWallets[2].ValoperAddress, txAmountUatom(), "--from", s.Chain.ValidatorWallets[0].Address)) - }) - - s.Run("generic", func() { - _, err := s.Chain.Validators[0].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[0].Moniker, - "authz", "grant", s.Chain.ValidatorWallets[1].Address, "generic", - "--msg-type", "/cosmos.gov.v1.MsgVote", - ) - s.Require().NoError(err) - - prop, err := s.Chain.BuildProposal(nil, "Test Proposal", "Test Proposal", "ipfs://CID", chainsuite.GovDepositAmount, s.Chain.ValidatorWallets[0].ValoperAddress, false) - s.Require().NoError(err) - result, err := s.Chain.SubmitProposal(s.GetContext(), s.Chain.ValidatorWallets[0].Moniker, prop) - s.Require().NoError(err) - s.Require().NoError(s.authzGenExec(s.GetContext(), s.Chain.ValidatorWallets[1], "gov", "vote", result.ProposalID, "yes", "--from", s.Chain.ValidatorWallets[0].Address)) - }) -} - -func (s TxSuite) TestFeegrant() { - const ( - granter = 5 - grantee = 1 - fundsReceiver = 2 - ) - - tests := []struct { - name string - revoke func(expireTime time.Time) - }{ - { - name: "revoke", - revoke: func(_ time.Time) { - _, err := s.Chain.Validators[granter].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[granter].Moniker, - "feegrant", "revoke", s.Chain.ValidatorWallets[granter].Address, s.Chain.ValidatorWallets[grantee].Address, - ) - s.Require().NoError(err) - }, - }, - { - name: "expire", - revoke: func(expire time.Time) { - <-time.After(time.Until(expire)) - err := testutil.WaitForBlocks(s.GetContext(), 1, s.Chain) - s.Require().NoError(err) - }, - }, - } - - for _, tt := range tests { - tt := tt - s.Run(tt.name, func() { - expire := time.Now().Add(20 * chainsuite.CommitTimeout) - _, err := s.Chain.Validators[granter].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[granter].Moniker, - "feegrant", "grant", s.Chain.ValidatorWallets[granter].Address, s.Chain.ValidatorWallets[grantee].Address, - "--expiration", expire.Format(time.RFC3339), - ) - s.Require().NoError(err) - - granterBalanceBefore, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[granter].Address, chainsuite.Uatom) - s.Require().NoError(err) - granteeBalanceBefore, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[grantee].Address, chainsuite.Uatom) - s.Require().NoError(err) - - _, err = s.Chain.Validators[grantee].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[grantee].Moniker, - "bank", "send", s.Chain.ValidatorWallets[grantee].Address, s.Chain.ValidatorWallets[fundsReceiver].Address, txAmountUatom(), - "--fee-granter", s.Chain.ValidatorWallets[granter].Address, - ) - s.Require().NoError(err) - - granteeBalanceAfter, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[grantee].Address, chainsuite.Uatom) - s.Require().NoError(err) - granterBalanceAfter, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[granter].Address, chainsuite.Uatom) - s.Require().NoError(err) - - s.Require().True(granterBalanceAfter.LT(granterBalanceBefore), "granterBalanceBefore: %s, granterBalanceAfter: %s", granterBalanceBefore, granterBalanceAfter) - s.Require().True(granteeBalanceAfter.Equal(granteeBalanceBefore.Sub(sdkmath.NewInt(txAmount))), "granteeBalanceBefore: %s, granteeBalanceAfter: %s", granteeBalanceBefore, granteeBalanceAfter) - - tt.revoke(expire) - - _, err = s.Chain.Validators[1].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[grantee].Moniker, - "bank", "send", s.Chain.ValidatorWallets[1].Address, s.Chain.ValidatorWallets[fundsReceiver].Address, txAmountUatom(), - "--fee-granter", s.Chain.ValidatorWallets[0].Address, - ) - s.Require().Error(err) - }) - } -} - -func (s *TxSuite) TestUnbondValidator() { - _, err := s.Chain.Validators[5].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[5].Moniker, - "staking", "unbond-validator", - ) - s.Require().NoError(err) - validator, err := s.Chain.StakingQueryValidator(s.GetContext(), s.Chain.ValidatorWallets[5].ValoperAddress) - s.Require().NoError(err) - s.Require().Equal(stakingtypes.Unbonding, validator.Status) - - _, err = s.Chain.Validators[5].ExecTx( - s.GetContext(), - s.Chain.ValidatorWallets[5].Moniker, - "slashing", "unjail", - ) - s.Require().NoError(err) - - validator, err = s.Chain.StakingQueryValidator(s.GetContext(), s.Chain.ValidatorWallets[5].ValoperAddress) - s.Require().NoError(err) - s.Require().Equal(stakingtypes.Bonded, validator.Status) -} - -func (s *TxSuite) TestMultisig() { - pubkey1, _, err := s.Chain.Validators[1].ExecBin(s.GetContext(), "keys", "show", s.Chain.ValidatorWallets[1].Moniker, "--pubkey", "--keyring-backend", "test") - s.Require().NoError(err) - - pubkey2, _, err := s.Chain.Validators[2].ExecBin(s.GetContext(), "keys", "show", s.Chain.ValidatorWallets[2].Moniker, "--pubkey", "--keyring-backend", "test") - s.Require().NoError(err) - - _, _, err = s.Chain.Validators[0].ExecBin(s.GetContext(), "keys", "add", "val1", "--pubkey", string(pubkey1), "--keyring-backend", "test") - s.Require().NoError(err) - - _, _, err = s.Chain.Validators[0].ExecBin(s.GetContext(), "keys", "add", "val2", "--pubkey", string(pubkey2), "--keyring-backend", "test") - s.Require().NoError(err) - - multisigName := "multisig" - _, _, err = s.Chain.Validators[0].ExecBin(s.GetContext(), "keys", "add", multisigName, "--multisig", fmt.Sprintf("%s,val1,val2", s.Chain.ValidatorWallets[0].Moniker), "--multisig-threshold", "2", "--keyring-backend", "test") - s.Require().NoError(err) - - pubkeyMulti, _, err := s.Chain.Validators[0].ExecBin(s.GetContext(), "keys", "show", multisigName, "--pubkey", "--keyring-backend", "test") - s.Require().NoError(err) - - _, _, err = s.Chain.Validators[1].ExecBin(s.GetContext(), "keys", "add", multisigName, "--pubkey", string(pubkeyMulti), "--keyring-backend", "test") - s.Require().NoError(err) - _, _, err = s.Chain.Validators[2].ExecBin(s.GetContext(), "keys", "add", multisigName, "--pubkey", string(pubkeyMulti), "--keyring-backend", "test") - s.Require().NoError(err) - // bogus validator, not in the multisig - _, _, err = s.Chain.Validators[4].ExecBin(s.GetContext(), "keys", "add", multisigName, "--pubkey", string(pubkeyMulti), "--keyring-backend", "test") - s.Require().NoError(err) - - defer func() { - _, _, err = s.Chain.Validators[0].ExecBin(s.GetContext(), "keys", "delete", "val1", "--keyring-backend", "test", "-y") - s.Require().NoError(err) - _, _, err = s.Chain.Validators[0].ExecBin(s.GetContext(), "keys", "delete", "val2", "--keyring-backend", "test", "-y") - s.Require().NoError(err) - for i := 0; i < 3; i++ { - _, _, err = s.Chain.Validators[i].ExecBin(s.GetContext(), "keys", "delete", multisigName, "--keyring-backend", "test", "-y") - s.Require().NoError(err) - } - _, _, err = s.Chain.Validators[4].ExecBin(s.GetContext(), "keys", "delete", multisigName, "--keyring-backend", "test", "-y") - s.Require().NoError(err) - }() - - multisigAddr, err := s.Chain.Validators[0].KeyBech32(s.GetContext(), multisigName, "") - s.Require().NoError(err) - - err = s.Chain.SendFunds(s.GetContext(), interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ - Denom: chainsuite.Uatom, - Amount: sdkmath.NewInt(chainsuite.ValidatorFunds), - Address: multisigAddr, - }) - s.Require().NoError(err) - - balanceBefore, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[3].Address, chainsuite.Uatom) - s.Require().NoError(err) - - txjson, err := s.Chain.GenerateTx( - s.GetContext(), 0, "bank", "send", multisigName, s.Chain.ValidatorWallets[3].Address, txAmountUatom(), - "--gas", "auto", "--gas-adjustment", fmt.Sprint(s.Chain.Config().GasAdjustment), "--gas-prices", s.Chain.Config().GasPrices, - ) - s.Require().NoError(err) - - err = s.Chain.Validators[0].WriteFile(s.GetContext(), []byte(txjson), "tx.json") - s.Require().NoError(err) - - signed0, _, err := s.Chain.Validators[0].Exec(s.GetContext(), - s.Chain.Validators[0].TxCommand(s.Chain.ValidatorWallets[0].Moniker, "sign", - path.Join(s.Chain.Validators[0].HomeDir(), "tx.json"), - "--multisig", multisigAddr, - ), nil) - s.Require().NoError(err) - - err = s.Chain.Validators[1].WriteFile(s.GetContext(), []byte(txjson), "tx.json") - s.Require().NoError(err) - - signed1, _, err := s.Chain.Validators[1].Exec(s.GetContext(), - s.Chain.Validators[1].TxCommand(s.Chain.ValidatorWallets[1].Moniker, "sign", - path.Join(s.Chain.Validators[1].HomeDir(), "tx.json"), - "--multisig", multisigAddr, - ), nil) - s.Require().NoError(err) - - err = s.Chain.Validators[4].WriteFile(s.GetContext(), []byte(txjson), "tx.json") - s.Require().NoError(err) - _, _, err = s.Chain.Validators[4].Exec(s.GetContext(), - s.Chain.Validators[4].TxCommand(s.Chain.ValidatorWallets[4].Moniker, "sign", - path.Join(s.Chain.Validators[4].HomeDir(), "tx.json"), - "--multisig", multisigAddr, - ), nil) - s.Require().Error(err) - - err = s.Chain.Validators[0].WriteFile(s.GetContext(), signed0, "signed0.json") - s.Require().NoError(err) - - _, _, err = s.Chain.Validators[0].Exec(s.GetContext(), s.Chain.Validators[0].TxCommand( - multisigName, - "multisign", - path.Join(s.Chain.Validators[0].HomeDir(), "tx.json"), - multisigName, - path.Join(s.Chain.Validators[0].HomeDir(), "signed0.json"), - ), nil) - s.Require().NoError(err) - - err = s.Chain.Validators[0].WriteFile(s.GetContext(), signed1, "signed1.json") - s.Require().NoError(err) - - multisign, _, err := s.Chain.Validators[0].Exec(s.GetContext(), s.Chain.Validators[0].TxCommand( - multisigName, - "multisign", - path.Join(s.Chain.Validators[0].HomeDir(), "tx.json"), - multisigName, - path.Join(s.Chain.Validators[0].HomeDir(), "signed0.json"), - path.Join(s.Chain.Validators[0].HomeDir(), "signed1.json"), - ), nil) - s.Require().NoError(err) - - err = s.Chain.Validators[0].WriteFile(s.GetContext(), multisign, "multisign.json") - s.Require().NoError(err) - - _, err = s.Chain.Validators[0].ExecTx(s.GetContext(), multisigName, "broadcast", path.Join(s.Chain.Validators[0].HomeDir(), "multisign.json")) - s.Require().NoError(err) - balanceAfter, err := s.Chain.GetBalance(s.GetContext(), s.Chain.ValidatorWallets[3].Address, chainsuite.Uatom) - s.Require().NoError(err) - s.Require().Equal(balanceBefore.Add(sdkmath.NewInt(txAmount)), balanceAfter) -} - -func TestTransactions(t *testing.T) { - txSuite := TxSuite{chainsuite.NewSuite(chainsuite.SuiteConfig{UpgradeOnSetup: true})} - suite.Run(t, &txSuite) -} - -func (s TxSuite) authzGenExec(ctx context.Context, grantee chainsuite.ValidatorWallet, command ...string) error { - txjson, err := s.Chain.GenerateTx(ctx, 1, command...) - s.Require().NoError(err) - - err = s.Chain.Validators[1].WriteFile(ctx, []byte(txjson), "tx.json") - s.Require().NoError(err) - - _, err = s.Chain.Validators[1].ExecTx( - ctx, - grantee.Moniker, - "authz", "exec", path.Join(s.Chain.Validators[1].HomeDir(), "tx.json"), - ) - return err -} diff --git a/tests/interchain/consensus_test.go b/tests/interchain/validator/inactive_validator_test.go similarity index 91% rename from tests/interchain/consensus_test.go rename to tests/interchain/validator/inactive_validator_test.go index a256731e4b9..56faff11d84 100644 --- a/tests/interchain/consensus_test.go +++ b/tests/interchain/validator/inactive_validator_test.go @@ -1,4 +1,4 @@ -package interchain_test +package validator_test import ( "encoding/json" @@ -20,12 +20,12 @@ const ( maxProviderConsensusValidators = 4 ) -type ConsensusSuite struct { +type InactiveValidatorsSuite struct { *chainsuite.Suite Consumer *chainsuite.Chain } -func (s *ConsensusSuite) SetupSuite() { +func (s *InactiveValidatorsSuite) SetupSuite() { s.Suite.SetupSuite() authority, err := s.Chain.GetGovernanceAddress(s.GetContext()) s.Require().NoError(err) @@ -94,7 +94,7 @@ func (s *ConsensusSuite) SetupSuite() { cfg := chainsuite.ConsumerConfig{ ChainName: "ics-consumer", Version: "v6.2.1", - ShouldCopyProviderKey: allProviderKeysCopied(), + ShouldCopyProviderKey: []bool{true, true, true, true, true, true}, Denom: chainsuite.Ucon, TopN: 100, AllowInactiveVals: true, @@ -119,7 +119,7 @@ func (s *ConsensusSuite) SetupSuite() { } // This is called 0ValidatorSets because it should run first; if the validator sets are wrong, obviously the other tests will fail -func (s *ConsensusSuite) Test0ValidatorSets() { +func (s *InactiveValidatorsSuite) Test0ValidatorSets() { vals, err := s.Chain.QueryJSON(s.GetContext(), "validators", "tendermint-validator-set") s.Require().NoError(err) s.Require().Equal(maxProviderConsensusValidators, len(vals.Array()), vals) @@ -139,20 +139,20 @@ func (s *ConsensusSuite) Test0ValidatorSets() { } } -func (s *ConsensusSuite) TestProviderJailing() { +func (s *InactiveValidatorsSuite) TestProviderJailing() { for i := 1; i < maxProviderConsensusValidators; i++ { jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Chain, i) s.Require().NoError(err) s.Assert().True(jailed, "validator %d should be jailed", i) } - for i := maxProviderConsensusValidators; i < chainsuite.ValidatorCount; i++ { + for i := maxProviderConsensusValidators; i < len(s.Chain.Validators); i++ { jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Chain, i) s.Require().NoError(err) s.Assert().False(jailed, "validator %d should not be jailed", i) } } -func (s *ConsensusSuite) TestConsumerJailing() { +func (s *InactiveValidatorsSuite) TestConsumerJailing() { for i := 1; i < maxProviderConsensusValidators; i++ { jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Consumer, i) s.Require().NoError(err) @@ -161,14 +161,14 @@ func (s *ConsensusSuite) TestConsumerJailing() { // Validator 4 will have been opted in automatically when the other ones went down _, err := s.Chain.Validators[maxProviderConsensusValidators].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[maxProviderConsensusValidators].Moniker, "provider", "opt-out", s.getConsumerID()) s.Require().NoError(err) - for i := maxProviderConsensusValidators; i < chainsuite.ValidatorCount; i++ { + for i := maxProviderConsensusValidators; i < len(s.Chain.Validators); i++ { jailed, err := s.Chain.IsValidatorJailedForConsumerDowntime(s.GetContext(), s.Relayer, s.Consumer, i) s.Require().NoError(err) s.Assert().False(jailed, "validator %d should not be jailed", i) } } -func (s *ConsensusSuite) TestOptInInactive() { +func (s *InactiveValidatorsSuite) TestOptInInactive() { consumerID := s.getConsumerID() // Validator 4 will have been opted in automatically when the other ones went down _, err := s.Chain.Validators[maxProviderConsensusValidators].ExecTx(s.GetContext(), s.Chain.ValidatorWallets[maxProviderConsensusValidators].Moniker, "provider", "opt-out", s.getConsumerID()) @@ -210,16 +210,21 @@ func (s *ConsensusSuite) TestOptInInactive() { s.Assert().False(jailed, "validator 5 should not be jailed") } -func (s *ConsensusSuite) getConsumerID() string { +func (s *InactiveValidatorsSuite) getConsumerID() string { consumerIDJSON, err := s.Chain.QueryJSON(s.GetContext(), fmt.Sprintf("chains.#(chain_id=%q).consumer_id", s.Consumer.Config().ChainID), "provider", "list-consumer-chains") s.Require().NoError(err) consumerID := consumerIDJSON.String() return consumerID } -func TestConsensus(t *testing.T) { - s := &ConsensusSuite{ - Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{CreateRelayer: true}), +func TestInactiveValidators(t *testing.T) { + s := &InactiveValidatorsSuite{ + Suite: chainsuite.NewSuite(chainsuite.SuiteConfig{ + CreateRelayer: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + }, + }), } suite.Run(t, s) } diff --git a/tests/interchain/validator/unbond_test.go b/tests/interchain/validator/unbond_test.go new file mode 100644 index 00000000000..19157e45c4e --- /dev/null +++ b/tests/interchain/validator/unbond_test.go @@ -0,0 +1,49 @@ +package validator_test + +import ( + "testing" + + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/gaia/v21/tests/interchain/chainsuite" + + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type UnbondingSuite struct { + *chainsuite.Suite +} + +func (s *UnbondingSuite) TestUnbondValidator() { + _, err := s.Chain.Validators[5].ExecTx( + s.GetContext(), + s.Chain.ValidatorWallets[5].Moniker, + "staking", "unbond-validator", + ) + s.Require().NoError(err) + validator, err := s.Chain.StakingQueryValidator(s.GetContext(), s.Chain.ValidatorWallets[5].ValoperAddress) + s.Require().NoError(err) + s.Require().Equal(stakingtypes.Unbonding, validator.Status) + + _, err = s.Chain.Validators[5].ExecTx( + s.GetContext(), + s.Chain.ValidatorWallets[5].Moniker, + "slashing", "unjail", + ) + s.Require().NoError(err) + + validator, err = s.Chain.StakingQueryValidator(s.GetContext(), s.Chain.ValidatorWallets[5].ValoperAddress) + s.Require().NoError(err) + s.Require().Equal(stakingtypes.Bonded, validator.Status) +} + +func TestUnbonding(t *testing.T) { + txSuite := UnbondingSuite{chainsuite.NewSuite(chainsuite.SuiteConfig{ + UpgradeOnSetup: true, + ChainSpec: &interchaintest.ChainSpec{ + NumValidators: &chainsuite.SixValidators, + }, + })} + suite.Run(t, &txSuite) +}