diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 61874cffd9..0c10af7174 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -3309,3 +3309,95 @@ func (tr Commands) AssignConsumerPubKey(action e2e.AssignConsumerPubKeyAction, g return cmd.CombinedOutput() } + +type CreateIbcClientAction struct { + ChainA ChainID + ChainB ChainID +} + +func (tr Chain) createIbcClientHermes( + action CreateIbcClientAction, + verbose bool, +) { + cmd := tr.target.ExecCommand("hermes", + "create", "client", + "--host-chain", string(tr.testConfig.chainConfigs[action.ChainA].ChainId), + "--reference-chain", string(tr.testConfig.chainConfigs[action.ChainB].ChainId), + "--trusting-period", "1200000s", + ) + + cmdReader, err := cmd.StdoutPipe() + if err != nil { + log.Fatal(err) + } + cmd.Stderr = cmd.Stdout + + if err := cmd.Start(); err != nil { + log.Fatal(err) + } + + scanner := bufio.NewScanner(cmdReader) + + for scanner.Scan() { + out := scanner.Text() + if verbose { + fmt.Println("createIbcClientHermes: " + out) + } + if out == done { + break + } + } + if err := scanner.Err(); err != nil { + log.Fatal(err) + } +} + +type TransferIbcTokenAction struct { + Chain ChainID + DstAddr string + From ValidatorID + Amount uint + Channel uint + Memo string +} + +func (tr Chain) transferIbcToken( + action TransferIbcTokenAction, + verbose bool, +) { + // Note: to get error response reported back from this command '--gas auto' needs to be set. + gas := "auto" + + transferCmd := fmt.Sprintf( + `%s tx ibc-transfer transfer transfer \ +%s %s %s --memo %q --from validator%s --chain-id %s \ +--home %s --node %s --gas %s --keyring-backend test -y -o json`, + tr.testConfig.chainConfigs[action.Chain].BinaryName, + "channel-"+fmt.Sprint(action.Channel), + action.DstAddr, + fmt.Sprint(action.Amount)+`stake`, + action.Memo, + action.From, + string(tr.testConfig.chainConfigs[action.Chain].ConsumerId), + tr.getValidatorHome(action.Chain, action.From), + tr.getValidatorNode(action.Chain, action.From), + gas, + ) + + cmd := tr.target.ExecCommand( + "/bin/bash", "-c", + transferCmd, + ) + + if verbose { + fmt.Println("transferIbcToken cmd:", cmd.String()) + } + + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("unexpected error during IBC token transfer: %s: %s", string(bz), err) + } + + // wait for inclusion in a block -> '--broadcast-mode block' is deprecated + tr.waitBlocks(action.Chain, 2, 30*time.Second) +} diff --git a/tests/e2e/steps_democracy.go b/tests/e2e/steps_democracy.go index df506639e4..6ea15b75b9 100644 --- a/tests/e2e/steps_democracy.go +++ b/tests/e2e/steps_democracy.go @@ -1,8 +1,6 @@ package main -import ( - gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1" -) +import gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1" const consumerRewardDenom = "ibc/3C3D7B3BE4ECC85A0E5B52A3AEC3B7DFC2AA9CA47C37821E57020D6807043BE9" @@ -176,6 +174,81 @@ func stepsDemocracy(consumerName string, expectRegisteredRewardDistribution bool }, }, }, + // Check that consumer rewards are only distributed + // if they come from a consumer client associated with a consumer id. + // + // Create a second consumer client on the provider + { + Action: CreateIbcClientAction{ + ChainA: ChainID("provi"), + ChainB: ChainID(consumerName), + }, + State: State{}, + }, + // Create a second IBC connection between the 2nd consumer client + // and the existing provider client on the consumer + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("provi"), + ChainB: ChainID(consumerName), + ClientA: 1, + ClientB: 0, // already created during the CCV handshake + }, + State: State{}, + }, + // Create a second IBC transfer channel + { + Action: AddIbcChannelAction{ + ChainA: ChainID("provi"), + ChainB: ChainID(consumerName), + ConnectionA: 1, + PortA: "transfer", + PortB: "transfer", + Order: "unordered", + Version: "ics20-1", + }, + State: State{}, + }, + // Transfer tokens from the consumer to the consumer reward pool + // of the provider via the 2nd transfer channel + { + Action: TransferIbcTokenAction{ + Chain: ChainID(consumerName), + From: ValidatorID("carol"), + DstAddr: "cosmos1ap0mh6xzfn8943urr84q6ae7zfnar48am2erhd", // consumer reward pool address + Amount: 1000000, + Channel: 2, // new transfer channel + Memo: "consumer chain rewards distribution", // use old memo + }, + State: State{}, + }, + // Relay the transfer packet from the second transfer + // channel and check that tokens are not distributed + // since the packet isn't associated to a consumer id + { + Action: RelayRewardPacketsToProviderAction{ + ConsumerChain: ChainID(consumerName), + ProviderChain: ChainID("provi"), + Port: "transfer", + Channel: 2, + }, + State: State{ + ChainID("provi"): ChainState{ + + Rewards: &Rewards{ + IsRewarded: map[ValidatorID]bool{ + ValidatorID("alice"): false, + ValidatorID("bob"): false, + ValidatorID("carol"): false, + }, + IsIncrementalReward: false, + IsNativeDenom: false, + }, + }, + }, + }, + // Relay pending consumer rewards sent via the 1st transfer channel + // and check that they are distributed to the validators. { Action: RelayRewardPacketsToProviderAction{ ConsumerChain: ChainID(consumerName), @@ -185,9 +258,9 @@ func stepsDemocracy(consumerName string, expectRegisteredRewardDistribution bool }, State: State{ ChainID("provi"): ChainState{ - // Check that ARE NOT minted and sent to provider chain and distributed to validators and their delegators on provider chain - // the tokens are not sent because the test configuration does not allow sending tokens Rewards: &Rewards{ + // expectRegisteredRewardDistribution == true + // expect "provi" validators to get rewards from "democ" IsRewarded: map[ValidatorID]bool{ ValidatorID("alice"): expectRegisteredRewardDistribution, ValidatorID("bob"): expectRegisteredRewardDistribution, diff --git a/tests/e2e/test_driver.go b/tests/e2e/test_driver.go index 2c3f9c1ac2..b20c194072 100644 --- a/tests/e2e/test_driver.go +++ b/tests/e2e/test_driver.go @@ -413,6 +413,13 @@ func (td *DefaultDriver) runAction(action interface{}) error { case SubmitConsumerMisbehaviourAction: target := td.getTargetDriver("provider") target.submitConsumerMisbehaviour(action, td.verbose) + case CreateIbcClientAction: + // use default for hermes actions + target := td.getTargetDriver("") + target.createIbcClientHermes(action, td.verbose) + case TransferIbcTokenAction: + target := td.getTargetDriver(action.Chain) + target.transferIbcToken(action, td.verbose) default: log.Fatalf("unknown action in testRun %s: %#v", td.testCfg.name, action) } diff --git a/x/ccv/consumer/keeper/distribution.go b/x/ccv/consumer/keeper/distribution.go index ecd8a6db1c..27dcf2cdc4 100644 --- a/x/ccv/consumer/keeper/distribution.go +++ b/x/ccv/consumer/keeper/distribution.go @@ -125,6 +125,7 @@ func (k Keeper) SendRewardsToProvider(ctx sdk.Context) error { if err != nil { return err } + // iterate over all whitelisted reward denoms for _, denom := range k.AllowedRewardDenoms(ctx) { // get the balance of the denom in the toSendToProviderTokens address diff --git a/x/ccv/provider/ibc_middleware.go b/x/ccv/provider/ibc_middleware.go index 7a0272b0ba..606281def6 100644 --- a/x/ccv/provider/ibc_middleware.go +++ b/x/ccv/provider/ibc_middleware.go @@ -135,8 +135,6 @@ func (im IBCMiddleware) OnRecvPacket( } consumerId := "" - logger.Info("received packet transfer memo:%#+v", data.Memo) - // check if the transfer has the reward memo if rewardMemo, err := ccvtypes.GetRewardMemoFromTransferMemo(data.Memo); err != nil { // check if the transfer is on a channel with the same underlying