diff --git a/app/provider/app.go b/app/provider/app.go index 7527756d45..9cf45fe7e3 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -102,6 +102,7 @@ import ( appencoding "github.com/cosmos/interchain-security/v4/app/encoding" testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + "github.com/cosmos/interchain-security/v4/x/ccv/provider" ibcprovider "github.com/cosmos/interchain-security/v4/x/ccv/provider" ibcproviderclient "github.com/cosmos/interchain-security/v4/x/ccv/provider/client" ibcproviderkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" @@ -470,12 +471,15 @@ func New( app.BankKeeper, scopedTransferKeeper, ) - transferModule := transfer.NewAppModule(app.TransferKeeper) - ibcmodule := transfer.NewIBCModule(app.TransferKeeper) + + // Add an IBC middleware callback to track the consumer rewards + var transferStack porttypes.IBCModule + transferStack = transfer.NewIBCModule(app.TransferKeeper) + transferStack = provider.NewIBCMiddleware(transferStack, app.ProviderKeeper) // create static IBC router, add transfer route, then set and seal it ibcRouter := porttypes.NewRouter() - ibcRouter.AddRoute(ibctransfertypes.ModuleName, ibcmodule) + ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack) ibcRouter.AddRoute(providertypes.ModuleName, providerModule) app.IBCKeeper.SetRouter(ibcRouter) @@ -514,7 +518,7 @@ func New( evidence.NewAppModule(app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), params.NewAppModule(app.ParamsKeeper), - transferModule, + transfer.NewAppModule(app.TransferKeeper), providerModule, ) @@ -610,7 +614,7 @@ func New( params.NewAppModule(app.ParamsKeeper), evidence.NewAppModule(app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), - transferModule, + transfer.NewAppModule(app.TransferKeeper), ) app.sm.RegisterStoreDecoders() diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 38be02864f..b4a512e36d 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -13,6 +13,8 @@ import "ibc/lightclients/tendermint/v1/tendermint.proto"; import "tendermint/crypto/keys.proto"; import "cosmos/evidence/v1beta1/evidence.proto"; import "cosmos/base/v1beta1/coin.proto"; +import "amino/amino.proto"; + // // Note any type defined in this file is ONLY used internally to the provider CCV module. @@ -300,3 +302,14 @@ message ConsumerAddrsToPrune { uint64 vsc_id = 2; AddressList consumer_addrs = 3; } + +// ConsumerRewardsAllocation stores the rewards allocated by a consumer chain +// to the consumer rewards pool. It is used to allocate the tokens to the consumer +// opted-in validators and the community pool during BeginBlock. +message ConsumerRewardsAllocation { + repeated cosmos.base.v1beta1.DecCoin rewards = 1 [ + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins" + ]; +} diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index 25cbcb3132..f06c6f9482 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -3,14 +3,22 @@ package integration import ( "strings" + "cosmossdk.io/math" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/bytes" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" icstestingutils "github.com/cosmos/interchain-security/v4/testutil/integration" consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) @@ -93,25 +101,41 @@ func (s *CCVTestSuite) TestRewardsDistribution() { s.Require().Greater(ibcCoinIndex, -1) // Check that the coins got into the ConsumerRewardsPool - s.Require().True(rewardCoins[ibcCoinIndex].Amount.Equal(providerExpectedRewards[0].Amount)) + s.Require().Equal(rewardCoins[ibcCoinIndex].Amount, (providerExpectedRewards[0].Amount)) // Advance a block and check that the coins are still in the ConsumerRewardsPool s.providerChain.NextBlock() rewardCoins = providerBankKeeper.GetAllBalances(s.providerCtx(), rewardPool) - s.Require().True(rewardCoins[ibcCoinIndex].Amount.Equal(providerExpectedRewards[0].Amount)) + s.Require().Equal(rewardCoins[ibcCoinIndex].Amount, (providerExpectedRewards[0].Amount)) // Set the consumer reward denom. This would be done by a governance proposal in prod s.providerApp.GetProviderKeeper().SetConsumerRewardDenom(s.providerCtx(), rewardCoins[ibcCoinIndex].Denom) - s.providerChain.NextBlock() + // Refill the consumer fee pool + err = consumerBankKeeper.SendCoinsFromAccountToModule(s.consumerCtx(), s.consumerChain.SenderAccount.GetAddress(), authtypes.FeeCollectorName, fees) + s.Require().NoError(err) + + // pass two blocks + s.consumerChain.NextBlock() + s.consumerChain.NextBlock() + + // transfer rewards from consumer to provider + relayAllCommittedPackets(s, s.consumerChain, s.transferPath, transfertypes.PortID, s.transferPath.EndpointA.ChannelID, 1) + + // check that the consumer rewards allocation are empty since relayAllCommittedPackets call BeginBlock + rewardsAlloc := s.providerApp.GetProviderKeeper().GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID) + s.Require().Empty(rewardsAlloc.Rewards) - // Check that the reward pool has no more coins because they were transferred to the fee pool + // Check that the reward pool still has the first coins transferred that were never allocated rewardCoins = providerBankKeeper.GetAllBalances(s.providerCtx(), rewardPool) - s.Require().Equal(0, len(rewardCoins)) + s.Require().Equal(rewardCoins[ibcCoinIndex].Amount, (providerExpectedRewards[0].Amount)) // check that the fee pool has the expected amount of coins + // Note that all rewards are allocated to the community pool since + // BeginBlock is called without the validators' votes in ibctesting. + // See NextBlock() in https://github.com/cosmos/ibc-go/blob/release/v7.3.x/testing/chain.go#L281 communityCoins := s.providerApp.GetTestDistributionKeeper().GetFeePoolCommunityCoins(s.providerCtx()) - s.Require().True(communityCoins[ibcCoinIndex].Amount.Equal(sdk.NewDecCoinFromCoin(providerExpectedRewards[0]).Amount)) + s.Require().Equal(communityCoins[ibcCoinIndex].Amount, (sdk.NewDecCoinFromCoin(providerExpectedRewards[0]).Amount)) } // TestSendRewardsRetries tests that failed reward transmissions are retried every BlocksPerDistributionTransmission blocks @@ -454,6 +478,397 @@ func (s *CCVTestSuite) TestSendRewardsToProvider() { } } +// TestIBCTransferMiddleware tests the logic of the IBC transfer OnRecvPacket callback +func (s *CCVTestSuite) TestIBCTransferMiddleware() { + + var ( + data ibctransfertypes.FungibleTokenPacketData + packet channeltypes.Packet + getIBCDenom func(string, string) string + ) + + testCases := []struct { + name string + setup func(sdk.Context, *providerkeeper.Keeper, icstestingutils.TestBankKeeper) + rewardsAllocated bool + expErr bool + }{ + { + "invalid IBC packet", + func(sdk.Context, *providerkeeper.Keeper, icstestingutils.TestBankKeeper) { + packet = channeltypes.Packet{} + }, + false, + true, + }, + { + "IBC packet sender isn't a consumer chain", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) { + // make the sender consumer chain impossible to identify + packet.DestinationChannel = "CorruptedChannelId" + }, + false, + false, + }, + { + "IBC Transfer recipient is not the consumer rewards pool address", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) { + data.Receiver = "cosmos149lw9fktlqfed3zt8ah48r5czmsug5s7kw77u9" // random acct address + packet.Data = data.GetBytes() + }, + false, + false, + }, + { + "IBC Transfer coin denom isn't registered", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) {}, + false, + false, + }, + { + "successful token transfer to empty pool", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) { + keeper.SetConsumerRewardDenom( + s.providerCtx(), + getIBCDenom(packet.DestinationPort, packet.DestinationChannel), + ) + }, + true, + false, + }, + { + "successful token transfer to filled pool", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) { + keeper.SetConsumerRewardDenom( + ctx, + getIBCDenom(packet.DestinationPort, packet.DestinationChannel), + ) + + // fill consumer reward pool + bankKeeper.SendCoinsFromAccountToModule( + ctx, + s.providerChain.SenderAccount.GetAddress(), + providertypes.ConsumerRewardsPool, + sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100_000))), + ) + // update consumer allocation + keeper.SetConsumerRewardsAllocation( + ctx, + s.consumerChain.ChainID, + providertypes.ConsumerRewardsAllocation{ + Rewards: sdk.NewDecCoins(sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(100_000))), + }, + ) + }, + true, + false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + s.SetupTest() + s.SetupCCVChannel(s.path) + s.SetupTransferChannel() + + providerKeeper := s.providerApp.GetProviderKeeper() + bankKeeper := s.providerApp.GetTestBankKeeper() + amount := sdk.NewInt(100) + + data = types.NewFungibleTokenPacketData( // can be explicitly changed in setup + sdk.DefaultBondDenom, + amount.String(), + authtypes.NewModuleAddress(consumertypes.ConsumerToSendToProviderName).String(), + providerKeeper.GetConsumerRewardsPoolAddressStr(s.providerCtx()), + "", + ) + + packet = channeltypes.NewPacket( // can be explicitly changed in setup + data.GetBytes(), + uint64(1), + s.transferPath.EndpointA.ChannelConfig.PortID, + s.transferPath.EndpointA.ChannelID, + s.transferPath.EndpointB.ChannelConfig.PortID, + s.transferPath.EndpointB.ChannelID, + clienttypes.NewHeight(1, 100), + 0, + ) + + providerKeeper.SetConsumerRewardDenom(s.providerCtx(), + transfertypes.GetPrefixedDenom( + packet.DestinationPort, + packet.DestinationChannel, + sdk.DefaultBondDenom, + ), + ) + + getIBCDenom = func(dstPort, dstChannel string) string { + return transfertypes.ParseDenomTrace( + transfertypes.GetPrefixedDenom( + packet.DestinationPort, + packet.DestinationChannel, + sdk.DefaultBondDenom, + ), + ).IBCDenom() + } + + tc.setup(s.providerCtx(), &providerKeeper, bankKeeper) + + cbs, ok := s.providerChain.App.GetIBCKeeper().Router.GetRoute(ibctransfertypes.ModuleName) + s.Require().True(ok) + + // save the IBC transfer rewards transferred + rewardsPoolBalance := bankKeeper.GetAllBalances(s.providerCtx(), sdk.MustAccAddressFromBech32(data.Receiver)) + + // save the consumer's rewards allocated + consumerRewardsAllocations := providerKeeper.GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID) + + // execute middleware OnRecvPacket logic + ack := cbs.OnRecvPacket(s.providerCtx(), packet, sdk.AccAddress{}) + + // compute expected rewards with provider denom + expRewards := sdk.Coin{ + Amount: amount, + Denom: getIBCDenom(packet.DestinationPort, packet.DestinationChannel), + } + + // compute the balance and allocation difference + rewardsTransferred := bankKeeper.GetAllBalances(s.providerCtx(), sdk.MustAccAddressFromBech32(data.Receiver)). + Sub(rewardsPoolBalance...) + rewardsAllocated := providerKeeper.GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID). + Rewards.Sub(consumerRewardsAllocations.Rewards) + + if !tc.expErr { + s.Require().True(ack.Success()) + // verify that the consumer rewards pool received the IBC coins + s.Require().Equal(rewardsTransferred, sdk.Coins{expRewards}) + + if tc.rewardsAllocated { + // check the data receiver address is set to the consumer rewards pool address + s.Require().Equal(data.GetReceiver(), providerKeeper.GetConsumerRewardsPoolAddressStr(s.providerCtx())) + + // verify that consumer rewards allocation is updated + s.Require().Equal(rewardsAllocated, sdk.NewDecCoinsFromCoins(expRewards)) + } else { + // verify that consumer rewards aren't allocated + s.Require().Empty(rewardsAllocated) + } + } else { + s.Require().False(ack.Success()) + } + }) + } +} + +// TestAllocateTokens is a happy-path test of the consumer rewards pool allocation +// to opted-in validators and the community pool +func (s *CCVTestSuite) TestAllocateTokens() { + // set up channel and delegate some tokens in order for validator set update to be sent to the consumer chain + s.SetupAllCCVChannels() + providerKeeper := s.providerApp.GetProviderKeeper() + bankKeeper := s.providerApp.GetTestBankKeeper() + distributionKeeper := s.providerApp.GetTestDistributionKeeper() + + totalRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))} + + // fund consumer rewards pool + bankKeeper.SendCoinsFromAccountToModule( + s.providerCtx(), + s.providerChain.SenderAccount.GetAddress(), + providertypes.ConsumerRewardsPool, + totalRewards, + ) + + // Allocate rewards evenly between consumers + rewardsPerConsumer := totalRewards.QuoInt(math.NewInt(int64(len(s.consumerBundles)))) + for chainID := range s.consumerBundles { + // update consumer allocation + providerKeeper.SetConsumerRewardsAllocation( + s.providerCtx(), + chainID, + providertypes.ConsumerRewardsAllocation{ + Rewards: sdk.NewDecCoinsFromCoins(rewardsPerConsumer...), + }, + ) + } + + // Iterate over the validators and + // store their current voting power and outstanding rewards + lastValOutRewards := map[string]sdk.DecCoins{} + votes := []abci.VoteInfo{} + for _, val := range s.providerChain.Vals.Validators { + votes = append(votes, + abci.VoteInfo{ + Validator: abci.Validator{Address: val.Address, Power: val.VotingPower}, + SignedLastBlock: true, + }, + ) + + valRewards := distributionKeeper.GetValidatorOutstandingRewards(s.providerCtx(), sdk.ValAddress(val.Address)) + lastValOutRewards[sdk.ValAddress(val.Address).String()] = valRewards.Rewards + } + + // store community pool balance + lastCommPool := distributionKeeper.GetFeePoolCommunityCoins(s.providerCtx()) + + // execute BeginBlock to trigger the token allocation + providerKeeper.BeginBlockRD( + s.providerCtx(), + abci.RequestBeginBlock{ + LastCommitInfo: abci.CommitInfo{ + Votes: votes, + }, + }, + ) + + valNum := len(s.providerChain.Vals.Validators) + consuNum := len(s.consumerBundles) + + // compute the expected validators token allocation by subtracting the community tax + rewardsPerConsumerDec := sdk.NewDecCoinsFromCoins(rewardsPerConsumer...) + communityTax := distributionKeeper.GetCommunityTax(s.providerCtx()) + validatorsExpRewards := rewardsPerConsumerDec. + MulDecTruncate(math.LegacyOneDec().Sub(communityTax)). + // multiply by the number of consumers since all the validators opted in + MulDec(sdk.NewDec(int64(consuNum))) + perValExpReward := validatorsExpRewards.QuoDec(sdk.NewDec(int64(valNum))) + + // verify the validator tokens allocation + // note all validators have the same voting power to keep things simple + for _, val := range s.providerChain.Vals.Validators { + valReward := distributionKeeper.GetValidatorOutstandingRewards(s.providerCtx(), sdk.ValAddress(val.Address)) + s.Require().Equal( + valReward.Rewards, + lastValOutRewards[sdk.ValAddress(val.Address).String()].Add(perValExpReward...), + ) + } + + commPoolExpRewards := sdk.NewDecCoinsFromCoins(totalRewards...).Sub(validatorsExpRewards) + currCommPool := distributionKeeper.GetFeePoolCommunityCoins(s.providerCtx()) + + s.Require().Equal(currCommPool, (lastCommPool.Add(commPoolExpRewards...))) +} + +// TestAllocateTokens is a unit-test for TransferConsumerRewardsToDistributionModule() +// but is written as an integration test to avoid excessive mocking. +func (s *CCVTestSuite) TransferConsumerRewardsToDistributionModule() { + testCases := []struct { + name string + rewardsPool sdk.Coins + rewardsAlloc sdk.DecCoins + expErr bool + }{ + { + "empty consumer rewards pool", + sdk.Coins{}, + sdk.DecCoins{}, + false, + }, + { + "empty consumer allocation", + sdk.Coins{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + sdk.DecCoins{}, + false, + }, + { + "equal consumer rewards pool and allocation", + sdk.Coins{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + sdk.DecCoins{ + sdk.NewDecCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + false, + }, + { + "less consumer rewards than allocation", + sdk.Coins{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(90)), + }, + sdk.DecCoins{ + sdk.NewDecCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + true, + }, + { + "remaining consumer rewards allocation", + sdk.Coins{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + sdk.DecCoins{ + sdk.DecCoin{ + Denom: sdk.DefaultBondDenom, + Amount: sdk.NewDecWithPrec(995, 1), + }, + }, + false, + }, + } + + providerKeeper := s.providerApp.GetProviderKeeper() + bankKeeper := s.providerApp.GetTestBankKeeper() + distributionKeeper := s.providerApp.GetTestDistributionKeeper() + + chainID := s.consumerChain.ChainID + + for _, tc := range testCases { + s.Run(tc.name, func() { + ctx, _ := s.providerCtx().CacheContext() + // fund consumer rewards pool + bankKeeper.SendCoinsFromAccountToModule( + ctx, + s.providerChain.SenderAccount.GetAddress(), + providertypes.ConsumerRewardsPool, + tc.rewardsPool, + ) + + // update consumer rewars allocation + providerKeeper.SetConsumerRewardsAllocation( + ctx, + chainID, + providertypes.ConsumerRewardsAllocation{ + Rewards: tc.rewardsAlloc, + }, + ) + + // store pool balance + oldPool := bankKeeper.GetAllBalances( + ctx, + distributionKeeper.GetDistributionAccount(ctx).GetAddress(), + ) + + // transfer consumer rewars to distribution module + coinsTransferred, err := providerKeeper.TransferConsumerRewardsToDistributionModule( + ctx, + chainID, + ) + if tc.expErr { + s.Require().Error(err) + return + } + + // check remaining consumer rewards allocation + expCoinTransferred, expRemaining := tc.rewardsAlloc.TruncateDecimal() + s.Require().Equal(expCoinTransferred, coinsTransferred) + + s.Require().Equal( + expRemaining, + providerKeeper.GetConsumerRewardsAllocation(ctx, chainID).Rewards, + ) + + // check updated consuemer rewards pool balance + newPool := bankKeeper.GetAllBalances( + ctx, + distributionKeeper.GetDistributionAccount(ctx).GetAddress(), + ) + + s.Require().Equal(newPool.Sub(oldPool...), coinsTransferred) + }) + } +} + // getEscrowBalance gets the current balances in the escrow account holding the transferred tokens to the provider func (s *CCVTestSuite) getEscrowBalance() sdk.Coins { consumerBankKeeper := s.consumerApp.GetTestBankKeeper() @@ -485,3 +900,82 @@ func (s *CCVTestSuite) prepareRewardDist() { blocksToGo := bpdt - blocksSinceLastTrans s.coordinator.CommitNBlocks(s.consumerChain, uint64(blocksToGo)) } + +func (s *CCVTestSuite) TestAllocateTokensToValidator() { + + providerkeepr := s.providerApp.GetProviderKeeper() + + chainID := "consumer" + + validators := []bytes.HexBytes{ + s.providerChain.Vals.Validators[0].Address, + s.providerChain.Vals.Validators[1].Address, + } + + votes := []abci.VoteInfo{ + {Validator: abci.Validator{Address: validators[0], Power: 1}}, + {Validator: abci.Validator{Address: validators[1], Power: 1}}, + } + + testCases := []struct { + name string + votes []abci.VoteInfo + tokens sdk.DecCoins + expCoinTransferred sdk.DecCoins + }{ + { + name: "reward tokens are empty", + }, + { + name: "total voting power is zero", + tokens: sdk.DecCoins{sdk.NewDecCoin("uatom", math.NewInt(100_000))}, + }, + { + name: "expect all tokens to be allocated to a single validator", + votes: []abci.VoteInfo{votes[0]}, + tokens: sdk.DecCoins{sdk.NewDecCoin("uatom", math.NewInt(100_000))}, + expCoinTransferred: sdk.DecCoins{sdk.NewDecCoin("uatom", math.NewInt(100_000))}, + }, + { + name: "expect tokens to be allocated evenly between validators", + votes: []abci.VoteInfo{votes[0], votes[1]}, + tokens: sdk.DecCoins{sdk.NewDecCoin("uatom", math.NewInt(555_555))}, + expCoinTransferred: sdk.DecCoins{sdk.NewDecCoin("uatom", math.NewInt(555_555))}, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + // TODO: opt validators in and verify + // that rewards are solely allocated to them + + ctx, _ := s.providerCtx().CacheContext() + + // allocate tokens + res := providerkeepr.AllocateTokensToConsumerValidators( + ctx, + chainID, + tc.votes, + tc.tokens, + ) + + // check that the expect result is returned + s.Require().Equal(tc.expCoinTransferred, res) + + if !tc.expCoinTransferred.Empty() { + // rewards are expected to be allocated evenly between validators + rewardsPerVal := tc.expCoinTransferred.QuoDec(sdk.NewDec(int64(len(tc.votes)))) + + // check that the rewards are allocated to validators as expected + for _, v := range tc.votes { + rewards := s.providerApp.GetTestDistributionKeeper().GetValidatorOutstandingRewards( + ctx, + sdk.ValAddress(v.Validator.Address), + ) + s.Require().Equal(rewardsPerVal, rewards.Rewards) + } + } + + }) + } +} diff --git a/testutil/integration/debug_test.go b/testutil/integration/debug_test.go index 77d460f05f..c370e8e701 100644 --- a/testutil/integration/debug_test.go +++ b/testutil/integration/debug_test.go @@ -283,3 +283,19 @@ func TestHandleConsumerDoubleVotingSlashesUndelegationsAndRelegations(t *testing func TestSlashRetries(t *testing.T) { runCCVTestByName(t, "TestSlashRetries") } + +func TestIBCTransferMiddleware(t *testing.T) { + runCCVTestByName(t, "TestIBCTransferMiddleware") +} + +func TestAllocateTokens(t *testing.T) { + runCCVTestByName(t, "TestAllocateTokens") +} + +func TestTransferConsumerRewardsToDistributionModule(t *testing.T) { + runCCVTestByName(t, "TransferConsumerRewardsToDistributionModule") +} + +func TestAllocateTokensToValidator(t *testing.T) { + runCCVTestByName(t, "TestAllocateTokensToValidator") +} diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index b183e8b377..a192765ec9 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -14,13 +14,14 @@ import ( types0 "github.com/cosmos/cosmos-sdk/types" types1 "github.com/cosmos/cosmos-sdk/x/auth/types" types2 "github.com/cosmos/cosmos-sdk/x/capability/types" + types3 "github.com/cosmos/cosmos-sdk/x/distribution/types" v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - types3 "github.com/cosmos/cosmos-sdk/x/slashing/types" - types4 "github.com/cosmos/cosmos-sdk/x/staking/types" - types5 "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - types6 "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - types7 "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - types8 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + types4 "github.com/cosmos/cosmos-sdk/x/slashing/types" + types5 "github.com/cosmos/cosmos-sdk/x/staking/types" + types6 "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + types7 "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + types8 "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + types9 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" exported "github.com/cosmos/ibc-go/v7/modules/core/exported" gomock "github.com/golang/mock/gomock" ) @@ -63,10 +64,10 @@ func (mr *MockStakingKeeperMockRecorder) BondDenom(ctx interface{}) *gomock.Call } // Delegation mocks base method. -func (m *MockStakingKeeper) Delegation(ctx types0.Context, addr types0.AccAddress, valAddr types0.ValAddress) types4.DelegationI { +func (m *MockStakingKeeper) Delegation(ctx types0.Context, addr types0.AccAddress, valAddr types0.ValAddress) types5.DelegationI { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Delegation", ctx, addr, valAddr) - ret0, _ := ret[0].(types4.DelegationI) + ret0, _ := ret[0].(types5.DelegationI) return ret0 } @@ -105,10 +106,10 @@ func (mr *MockStakingKeeperMockRecorder) GetLastValidatorPower(ctx, operator int } // GetLastValidators mocks base method. -func (m *MockStakingKeeper) GetLastValidators(ctx types0.Context) []types4.Validator { +func (m *MockStakingKeeper) GetLastValidators(ctx types0.Context) []types5.Validator { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLastValidators", ctx) - ret0, _ := ret[0].([]types4.Validator) + ret0, _ := ret[0].([]types5.Validator) return ret0 } @@ -119,10 +120,10 @@ func (mr *MockStakingKeeperMockRecorder) GetLastValidators(ctx interface{}) *gom } // GetRedelegationsFromSrcValidator mocks base method. -func (m *MockStakingKeeper) GetRedelegationsFromSrcValidator(ctx types0.Context, valAddr types0.ValAddress) []types4.Redelegation { +func (m *MockStakingKeeper) GetRedelegationsFromSrcValidator(ctx types0.Context, valAddr types0.ValAddress) []types5.Redelegation { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRedelegationsFromSrcValidator", ctx, valAddr) - ret0, _ := ret[0].([]types4.Redelegation) + ret0, _ := ret[0].([]types5.Redelegation) return ret0 } @@ -133,10 +134,10 @@ func (mr *MockStakingKeeperMockRecorder) GetRedelegationsFromSrcValidator(ctx, v } // GetUnbondingDelegationsFromValidator mocks base method. -func (m *MockStakingKeeper) GetUnbondingDelegationsFromValidator(ctx types0.Context, valAddr types0.ValAddress) []types4.UnbondingDelegation { +func (m *MockStakingKeeper) GetUnbondingDelegationsFromValidator(ctx types0.Context, valAddr types0.ValAddress) []types5.UnbondingDelegation { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUnbondingDelegationsFromValidator", ctx, valAddr) - ret0, _ := ret[0].([]types4.UnbondingDelegation) + ret0, _ := ret[0].([]types5.UnbondingDelegation) return ret0 } @@ -147,10 +148,10 @@ func (mr *MockStakingKeeperMockRecorder) GetUnbondingDelegationsFromValidator(ct } // GetUnbondingType mocks base method. -func (m *MockStakingKeeper) GetUnbondingType(ctx types0.Context, id uint64) (types4.UnbondingType, bool) { +func (m *MockStakingKeeper) GetUnbondingType(ctx types0.Context, id uint64) (types5.UnbondingType, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUnbondingType", ctx, id) - ret0, _ := ret[0].(types4.UnbondingType) + ret0, _ := ret[0].(types5.UnbondingType) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -162,10 +163,10 @@ func (mr *MockStakingKeeperMockRecorder) GetUnbondingType(ctx, id interface{}) * } // GetValidator mocks base method. -func (m *MockStakingKeeper) GetValidator(ctx types0.Context, addr types0.ValAddress) (types4.Validator, bool) { +func (m *MockStakingKeeper) GetValidator(ctx types0.Context, addr types0.ValAddress) (types5.Validator, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidator", ctx, addr) - ret0, _ := ret[0].(types4.Validator) + ret0, _ := ret[0].(types5.Validator) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -177,10 +178,10 @@ func (mr *MockStakingKeeperMockRecorder) GetValidator(ctx, addr interface{}) *go } // GetValidatorByConsAddr mocks base method. -func (m *MockStakingKeeper) GetValidatorByConsAddr(ctx types0.Context, consAddr types0.ConsAddress) (types4.Validator, bool) { +func (m *MockStakingKeeper) GetValidatorByConsAddr(ctx types0.Context, consAddr types0.ConsAddress) (types5.Validator, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidatorByConsAddr", ctx, consAddr) - ret0, _ := ret[0].(types4.Validator) + ret0, _ := ret[0].(types5.Validator) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -232,7 +233,7 @@ func (mr *MockStakingKeeperMockRecorder) IterateLastValidatorPowers(ctx, cb inte } // IterateValidators mocks base method. -func (m *MockStakingKeeper) IterateValidators(ctx types0.Context, f func(int64, types4.ValidatorI) bool) { +func (m *MockStakingKeeper) IterateValidators(ctx types0.Context, f func(int64, types5.ValidatorI) bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "IterateValidators", ctx, f) } @@ -312,7 +313,7 @@ func (mr *MockStakingKeeperMockRecorder) Slash(arg0, arg1, arg2, arg3, arg4 inte } // SlashRedelegation mocks base method. -func (m *MockStakingKeeper) SlashRedelegation(arg0 types0.Context, arg1 types4.Validator, arg2 types4.Redelegation, arg3 int64, arg4 types0.Dec) math.Int { +func (m *MockStakingKeeper) SlashRedelegation(arg0 types0.Context, arg1 types5.Validator, arg2 types5.Redelegation, arg3 int64, arg4 types0.Dec) math.Int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SlashRedelegation", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(math.Int) @@ -326,7 +327,7 @@ func (mr *MockStakingKeeperMockRecorder) SlashRedelegation(arg0, arg1, arg2, arg } // SlashUnbondingDelegation mocks base method. -func (m *MockStakingKeeper) SlashUnbondingDelegation(arg0 types0.Context, arg1 types4.UnbondingDelegation, arg2 int64, arg3 types0.Dec) math.Int { +func (m *MockStakingKeeper) SlashUnbondingDelegation(arg0 types0.Context, arg1 types5.UnbondingDelegation, arg2 int64, arg3 types0.Dec) math.Int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SlashUnbondingDelegation", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(math.Int) @@ -340,7 +341,7 @@ func (mr *MockStakingKeeperMockRecorder) SlashUnbondingDelegation(arg0, arg1, ar } // SlashWithInfractionReason mocks base method. -func (m *MockStakingKeeper) SlashWithInfractionReason(arg0 types0.Context, arg1 types0.ConsAddress, arg2, arg3 int64, arg4 types0.Dec, arg5 types4.Infraction) math.Int { +func (m *MockStakingKeeper) SlashWithInfractionReason(arg0 types0.Context, arg1 types0.ConsAddress, arg2, arg3 int64, arg4 types0.Dec, arg5 types5.Infraction) math.Int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SlashWithInfractionReason", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(math.Int) @@ -394,10 +395,10 @@ func (mr *MockStakingKeeperMockRecorder) Unjail(ctx, addr interface{}) *gomock.C } // Validator mocks base method. -func (m *MockStakingKeeper) Validator(ctx types0.Context, addr types0.ValAddress) types4.ValidatorI { +func (m *MockStakingKeeper) Validator(ctx types0.Context, addr types0.ValAddress) types5.ValidatorI { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Validator", ctx, addr) - ret0, _ := ret[0].(types4.ValidatorI) + ret0, _ := ret[0].(types5.ValidatorI) return ret0 } @@ -408,10 +409,10 @@ func (mr *MockStakingKeeperMockRecorder) Validator(ctx, addr interface{}) *gomoc } // ValidatorByConsAddr mocks base method. -func (m *MockStakingKeeper) ValidatorByConsAddr(ctx types0.Context, consAddr types0.ConsAddress) types4.ValidatorI { +func (m *MockStakingKeeper) ValidatorByConsAddr(ctx types0.Context, consAddr types0.ConsAddress) types5.ValidatorI { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidatorByConsAddr", ctx, consAddr) - ret0, _ := ret[0].(types4.ValidatorI) + ret0, _ := ret[0].(types5.ValidatorI) return ret0 } @@ -459,10 +460,10 @@ func (mr *MockSlashingKeeperMockRecorder) DowntimeJailDuration(arg0 interface{}) } // GetValidatorSigningInfo mocks base method. -func (m *MockSlashingKeeper) GetValidatorSigningInfo(ctx types0.Context, address types0.ConsAddress) (types3.ValidatorSigningInfo, bool) { +func (m *MockSlashingKeeper) GetValidatorSigningInfo(ctx types0.Context, address types0.ConsAddress) (types4.ValidatorSigningInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidatorSigningInfo", ctx, address) - ret0, _ := ret[0].(types3.ValidatorSigningInfo) + ret0, _ := ret[0].(types4.ValidatorSigningInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -500,7 +501,7 @@ func (mr *MockSlashingKeeperMockRecorder) JailUntil(arg0, arg1, arg2 interface{} } // SetValidatorSigningInfo mocks base method. -func (m *MockSlashingKeeper) SetValidatorSigningInfo(ctx types0.Context, address types0.ConsAddress, info types3.ValidatorSigningInfo) { +func (m *MockSlashingKeeper) SetValidatorSigningInfo(ctx types0.Context, address types0.ConsAddress, info types4.ValidatorSigningInfo) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetValidatorSigningInfo", ctx, address, info) } @@ -589,10 +590,10 @@ func (mr *MockChannelKeeperMockRecorder) ChanCloseInit(ctx, portID, channelID, c } // GetChannel mocks base method. -func (m *MockChannelKeeper) GetChannel(ctx types0.Context, srcPort, srcChan string) (types8.Channel, bool) { +func (m *MockChannelKeeper) GetChannel(ctx types0.Context, srcPort, srcChan string) (types9.Channel, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetChannel", ctx, srcPort, srcChan) - ret0, _ := ret[0].(types8.Channel) + ret0, _ := ret[0].(types9.Channel) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -635,7 +636,7 @@ func (mr *MockChannelKeeperMockRecorder) GetNextSequenceSend(ctx, portID, channe } // SendPacket mocks base method. -func (m *MockChannelKeeper) SendPacket(ctx types0.Context, chanCap *types2.Capability, sourcePort, sourceChannel string, timeoutHeight types6.Height, timeoutTimestamp uint64, data []byte) (uint64, error) { +func (m *MockChannelKeeper) SendPacket(ctx types0.Context, chanCap *types2.Capability, sourcePort, sourceChannel string, timeoutHeight types7.Height, timeoutTimestamp uint64, data []byte) (uint64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendPacket", ctx, chanCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) ret0, _ := ret[0].(uint64) @@ -724,10 +725,10 @@ func (m *MockConnectionKeeper) EXPECT() *MockConnectionKeeperMockRecorder { } // GetConnection mocks base method. -func (m *MockConnectionKeeper) GetConnection(ctx types0.Context, connectionID string) (types7.ConnectionEnd, bool) { +func (m *MockConnectionKeeper) GetConnection(ctx types0.Context, connectionID string) (types8.ConnectionEnd, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetConnection", ctx, connectionID) - ret0, _ := ret[0].(types7.ConnectionEnd) + ret0, _ := ret[0].(types8.ConnectionEnd) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -885,6 +886,18 @@ func (m *MockDistributionKeeper) EXPECT() *MockDistributionKeeperMockRecorder { return m.recorder } +// AllocateTokensToValidator mocks base method. +func (m *MockDistributionKeeper) AllocateTokensToValidator(ctx types0.Context, validator types5.ValidatorI, reward types0.DecCoins) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AllocateTokensToValidator", ctx, validator, reward) +} + +// AllocateTokensToValidator indicates an expected call of AllocateTokensToValidator. +func (mr *MockDistributionKeeperMockRecorder) AllocateTokensToValidator(ctx, validator, reward interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocateTokensToValidator", reflect.TypeOf((*MockDistributionKeeper)(nil).AllocateTokensToValidator), ctx, validator, reward) +} + // FundCommunityPool mocks base method. func (m *MockDistributionKeeper) FundCommunityPool(ctx types0.Context, amount types0.Coins, sender types0.AccAddress) error { m.ctrl.T.Helper() @@ -899,6 +912,46 @@ func (mr *MockDistributionKeeperMockRecorder) FundCommunityPool(ctx, amount, sen return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FundCommunityPool", reflect.TypeOf((*MockDistributionKeeper)(nil).FundCommunityPool), ctx, amount, sender) } +// GetCommunityTax mocks base method. +func (m *MockDistributionKeeper) GetCommunityTax(ctx types0.Context) math.LegacyDec { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCommunityTax", ctx) + ret0, _ := ret[0].(math.LegacyDec) + return ret0 +} + +// GetCommunityTax indicates an expected call of GetCommunityTax. +func (mr *MockDistributionKeeperMockRecorder) GetCommunityTax(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCommunityTax", reflect.TypeOf((*MockDistributionKeeper)(nil).GetCommunityTax), ctx) +} + +// GetFeePool mocks base method. +func (m *MockDistributionKeeper) GetFeePool(ctx types0.Context) types3.FeePool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFeePool", ctx) + ret0, _ := ret[0].(types3.FeePool) + return ret0 +} + +// GetFeePool indicates an expected call of GetFeePool. +func (mr *MockDistributionKeeperMockRecorder) GetFeePool(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFeePool", reflect.TypeOf((*MockDistributionKeeper)(nil).GetFeePool), ctx) +} + +// SetFeePool mocks base method. +func (m *MockDistributionKeeper) SetFeePool(ctx types0.Context, feePool types3.FeePool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetFeePool", ctx, feePool) +} + +// SetFeePool indicates an expected call of SetFeePool. +func (mr *MockDistributionKeeperMockRecorder) SetFeePool(ctx, feePool interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFeePool", reflect.TypeOf((*MockDistributionKeeper)(nil).SetFeePool), ctx, feePool) +} + // MockConsumerHooks is a mock of ConsumerHooks interface. type MockConsumerHooks struct { ctrl *gomock.Controller @@ -1061,11 +1114,139 @@ func (m *MockIBCTransferKeeper) EXPECT() *MockIBCTransferKeeperMockRecorder { return m.recorder } +// OnAcknowledgementPacket mocks base method. +func (m *MockIBCTransferKeeper) OnAcknowledgementPacket(ctx types0.Context, packet types9.Packet, acknowledgement []byte, relayer types0.AccAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnAcknowledgementPacket", ctx, packet, acknowledgement, relayer) + ret0, _ := ret[0].(error) + return ret0 +} + +// OnAcknowledgementPacket indicates an expected call of OnAcknowledgementPacket. +func (mr *MockIBCTransferKeeperMockRecorder) OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnAcknowledgementPacket", reflect.TypeOf((*MockIBCTransferKeeper)(nil).OnAcknowledgementPacket), ctx, packet, acknowledgement, relayer) +} + +// OnChanCloseConfirm mocks base method. +func (m *MockIBCTransferKeeper) OnChanCloseConfirm(ctx types0.Context, portID, channelID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnChanCloseConfirm", ctx, portID, channelID) + ret0, _ := ret[0].(error) + return ret0 +} + +// OnChanCloseConfirm indicates an expected call of OnChanCloseConfirm. +func (mr *MockIBCTransferKeeperMockRecorder) OnChanCloseConfirm(ctx, portID, channelID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnChanCloseConfirm", reflect.TypeOf((*MockIBCTransferKeeper)(nil).OnChanCloseConfirm), ctx, portID, channelID) +} + +// OnChanCloseInit mocks base method. +func (m *MockIBCTransferKeeper) OnChanCloseInit(ctx types0.Context, portID, channelID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnChanCloseInit", ctx, portID, channelID) + ret0, _ := ret[0].(error) + return ret0 +} + +// OnChanCloseInit indicates an expected call of OnChanCloseInit. +func (mr *MockIBCTransferKeeperMockRecorder) OnChanCloseInit(ctx, portID, channelID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnChanCloseInit", reflect.TypeOf((*MockIBCTransferKeeper)(nil).OnChanCloseInit), ctx, portID, channelID) +} + +// OnChanOpenAck mocks base method. +func (m *MockIBCTransferKeeper) OnChanOpenAck(ctx types0.Context, portID, channelID, counterpartyChannelID, counterpartyVersion string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnChanOpenAck", ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) + ret0, _ := ret[0].(error) + return ret0 +} + +// OnChanOpenAck indicates an expected call of OnChanOpenAck. +func (mr *MockIBCTransferKeeperMockRecorder) OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnChanOpenAck", reflect.TypeOf((*MockIBCTransferKeeper)(nil).OnChanOpenAck), ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) +} + +// OnChanOpenConfirm mocks base method. +func (m *MockIBCTransferKeeper) OnChanOpenConfirm(ctx types0.Context, portID, channelID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnChanOpenConfirm", ctx, portID, channelID) + ret0, _ := ret[0].(error) + return ret0 +} + +// OnChanOpenConfirm indicates an expected call of OnChanOpenConfirm. +func (mr *MockIBCTransferKeeperMockRecorder) OnChanOpenConfirm(ctx, portID, channelID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnChanOpenConfirm", reflect.TypeOf((*MockIBCTransferKeeper)(nil).OnChanOpenConfirm), ctx, portID, channelID) +} + +// OnChanOpenInit mocks base method. +func (m *MockIBCTransferKeeper) OnChanOpenInit(ctx types0.Context, order types9.Order, connectionHops []string, portID, channelID string, channelCap *types2.Capability, counterparty types9.Counterparty, version string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnChanOpenInit", ctx, order, connectionHops, portID, channelID, channelCap, counterparty, version) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// OnChanOpenInit indicates an expected call of OnChanOpenInit. +func (mr *MockIBCTransferKeeperMockRecorder) OnChanOpenInit(ctx, order, connectionHops, portID, channelID, channelCap, counterparty, version interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnChanOpenInit", reflect.TypeOf((*MockIBCTransferKeeper)(nil).OnChanOpenInit), ctx, order, connectionHops, portID, channelID, channelCap, counterparty, version) +} + +// OnChanOpenTry mocks base method. +func (m *MockIBCTransferKeeper) OnChanOpenTry(ctx types0.Context, order types9.Order, connectionHops []string, portID, channelID string, channelCap *types2.Capability, counterparty types9.Counterparty, counterpartyVersion string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnChanOpenTry", ctx, order, connectionHops, portID, channelID, channelCap, counterparty, counterpartyVersion) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// OnChanOpenTry indicates an expected call of OnChanOpenTry. +func (mr *MockIBCTransferKeeperMockRecorder) OnChanOpenTry(ctx, order, connectionHops, portID, channelID, channelCap, counterparty, counterpartyVersion interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnChanOpenTry", reflect.TypeOf((*MockIBCTransferKeeper)(nil).OnChanOpenTry), ctx, order, connectionHops, portID, channelID, channelCap, counterparty, counterpartyVersion) +} + +// OnRecvPacket mocks base method. +func (m *MockIBCTransferKeeper) OnRecvPacket(ctx types0.Context, packet types9.Packet, relayer types0.AccAddress) exported.Acknowledgement { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnRecvPacket", ctx, packet, relayer) + ret0, _ := ret[0].(exported.Acknowledgement) + return ret0 +} + +// OnRecvPacket indicates an expected call of OnRecvPacket. +func (mr *MockIBCTransferKeeperMockRecorder) OnRecvPacket(ctx, packet, relayer interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnRecvPacket", reflect.TypeOf((*MockIBCTransferKeeper)(nil).OnRecvPacket), ctx, packet, relayer) +} + +// OnTimeoutPacket mocks base method. +func (m *MockIBCTransferKeeper) OnTimeoutPacket(ctx types0.Context, packet types9.Packet, relayer types0.AccAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OnTimeoutPacket", ctx, packet, relayer) + ret0, _ := ret[0].(error) + return ret0 +} + +// OnTimeoutPacket indicates an expected call of OnTimeoutPacket. +func (mr *MockIBCTransferKeeperMockRecorder) OnTimeoutPacket(ctx, packet, relayer interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnTimeoutPacket", reflect.TypeOf((*MockIBCTransferKeeper)(nil).OnTimeoutPacket), ctx, packet, relayer) +} + // Transfer mocks base method. -func (m *MockIBCTransferKeeper) Transfer(arg0 context.Context, arg1 *types5.MsgTransfer) (*types5.MsgTransferResponse, error) { +func (m *MockIBCTransferKeeper) Transfer(arg0 context.Context, arg1 *types6.MsgTransfer) (*types6.MsgTransferResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Transfer", arg0, arg1) - ret0, _ := ret[0].(*types5.MsgTransferResponse) + ret0, _ := ret[0].(*types6.MsgTransferResponse) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1100,10 +1281,10 @@ func (m *MockIBCCoreKeeper) EXPECT() *MockIBCCoreKeeperMockRecorder { } // ChannelOpenInit mocks base method. -func (m *MockIBCCoreKeeper) ChannelOpenInit(goCtx context.Context, msg *types8.MsgChannelOpenInit) (*types8.MsgChannelOpenInitResponse, error) { +func (m *MockIBCCoreKeeper) ChannelOpenInit(goCtx context.Context, msg *types9.MsgChannelOpenInit) (*types9.MsgChannelOpenInitResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ChannelOpenInit", goCtx, msg) - ret0, _ := ret[0].(*types8.MsgChannelOpenInitResponse) + ret0, _ := ret[0].(*types9.MsgChannelOpenInitResponse) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/x/ccv/provider/ibc_middleware.go b/x/ccv/provider/ibc_middleware.go new file mode 100644 index 0000000000..1c1b1ce824 --- /dev/null +++ b/x/ccv/provider/ibc_middleware.go @@ -0,0 +1,240 @@ +package provider + +import ( + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" +) + +var _ porttypes.Middleware = &IBCMiddleware{} + +// IBCMiddleware implements the callbacks for the IBC transfer middleware given the +// provider keeper and the underlying application. +type IBCMiddleware struct { + app porttypes.IBCModule + keeper keeper.Keeper +} + +// NewIBCMiddleware creates a new IBCMiddlware given the keeper and underlying application +func NewIBCMiddleware(app porttypes.IBCModule, k keeper.Keeper) IBCMiddleware { + return IBCMiddleware{ + app: app, + keeper: k, + } +} + +// OnChanOpenInit implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) (string, error) { + // call underlying app's OnChanOpenInit callback with the appVersion + return im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version) +} + +// OnChanOpenTry implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + counterpartyVersion string, +) (string, error) { + // call underlying app's OnChanOpenTry callback with the appVersion + return im.app.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, counterpartyVersion) +} + +// OnChanOpenAck implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyChannelID string, + counterpartyVersion string, +) error { + // call underlying app's OnChanOpenAck callback with the counterparty app version. + return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) +} + +// OnChanOpenConfirm implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // call underlying app's OnChanOpenConfirm callback. + return im.app.OnChanOpenConfirm(ctx, portID, channelID) +} + +// OnChanCloseInit implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + // call underlying app's OnChanCloseInit callback. + return im.app.OnChanCloseInit(ctx, portID, channelID) +} + +// OnChanCloseConfirm implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + return im.app.OnChanCloseConfirm(ctx, portID, channelID) +} + +// OnRecvPacket executes the IBC transfer. In case of success, +// it verifies if the packet sender is a consumer chain +// and if the received IBC coin is whitelisted. In such instances, +// it appends the coin to the consumer's chain allocation record +func (im IBCMiddleware) OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) exported.Acknowledgement { + // executes the IBC transfer OnRecv logic + ack := im.app.OnRecvPacket(ctx, packet, relayer) + + // execute the middleware logic only if the sender is a consumer chain + consumerID, err := im.keeper.IdentifyConsumerChainIDFromIBCPacket(ctx, packet) + if err != nil { + return ack + } + + // Note that inside the below if condition statement, + // we know that the IBC transfer succeeded. That entails + // that the packet data is valid and can be safely + // deserialized without checking errors. + if ack.Success() { + // extract the coin info received from the packet data + var data ibctransfertypes.FungibleTokenPacketData + _ = types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data) + + // check if the recipient is the consumer reward's pool address + receiver, _ := sdk.AccAddressFromBech32(data.Receiver) + if receiver.String() != im.keeper.GetConsumerRewardsPoolAddressStr(ctx) { + return ack + } + + coinAmt, _ := math.NewIntFromString(data.Amount) + coinDenom := GetProviderDenom(data.Denom, packet) + + // verify that the coin's denom is a whitelisted consumer denom, + // and if so, adds it to the consumer chain rewards allocation, + // otherwise the prohibited coin just stays in the pool forever. + if im.keeper.ConsumerRewardDenomExists(ctx, coinDenom) { + alloc := im.keeper.GetConsumerRewardsAllocation(ctx, consumerID) + alloc.Rewards = alloc.Rewards.Add( + sdk.NewDecCoinsFromCoins(sdk.Coin{ + Denom: coinDenom, + Amount: coinAmt, + })...) + im.keeper.SetConsumerRewardsAllocation(ctx, consumerID, alloc) + } + } + + return ack +} + +// OnAcknowledgementPacket implements the IBCMiddleware interface +// If fees are not enabled, this callback will default to the ibc-core packet callback +func (im IBCMiddleware) OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + relayer sdk.AccAddress, +) error { + // call underlying app's OnAcknowledgementPacket callback. + return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) +} + +// OnTimeoutPacket implements the IBCMiddleware interface +// If fees are not enabled, this callback will default to the ibc-core packet callback +func (im IBCMiddleware) OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) error { + // call underlying app's OnTimeoutPacket callback. + return im.app.OnTimeoutPacket(ctx, packet, relayer) +} + +// SendPacket implements the ICS4 Wrapper interface +func (im IBCMiddleware) SendPacket( + sdk.Context, + *capabilitytypes.Capability, + string, + string, + clienttypes.Height, + uint64, + []byte, +) (uint64, error) { + panic("should never be called since the IBC middleware doesn't have an ICS4wrapper") +} + +// WriteAcknowledgement implements the ICS4 Wrapper interface +func (im IBCMiddleware) WriteAcknowledgement( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + ack exported.Acknowledgement, +) error { + panic("should never be called since the IBC middleware doesn't have an ICS4wrapper") +} + +// GetAppVersion returns the application version of the underlying application +func (im IBCMiddleware) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + panic("should never be called since the IBC middleware doesn't have an ICS4wrapper") +} + +// GetProviderDenom returns the updated given denom according to the given IBC packet +// It follows the same logic than the OnRecvPacket method of the IBC transfer module +// see https://github.com/cosmos/ibc-go/blob/v7.3.2/modules/apps/transfer/keeper/relay.go#L162 +func GetProviderDenom(denom string, packet channeltypes.Packet) (providerDenom string) { + // If the the prefix denom corresponds to the packet's source port and channel, + // returns the base denom + if ibctransfertypes.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), denom) { + voucherPrefix := ibctransfertypes.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel()) + unprefixedDenom := denom[len(voucherPrefix):] + + // coin denomination used in sending from the escrow address + providerDenom = unprefixedDenom + + // The denomination used to send the coins is either the native denom or the hash of the path + // if the denomination is not native. + denomTrace := ibctransfertypes.ParseDenomTrace(unprefixedDenom) + if denomTrace.Path != "" { + providerDenom = denomTrace.IBCDenom() + } + // update the prefix denom according to the packet info + } else { + prefixedDenom := ibctransfertypes.GetPrefixedDenom( + packet.GetDestPort(), + packet.GetDestChannel(), + denom, + ) + + providerDenom = ibctransfertypes.ParseDenomTrace(prefixedDenom).IBCDenom() + } + + return providerDenom +} diff --git a/x/ccv/provider/ibc_middleware_test.go b/x/ccv/provider/ibc_middleware_test.go new file mode 100644 index 0000000000..3701a65402 --- /dev/null +++ b/x/ccv/provider/ibc_middleware_test.go @@ -0,0 +1,76 @@ +package provider_test + +import ( + "testing" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider" + "github.com/stretchr/testify/require" +) + +func TestGetProviderDenom(t *testing.T) { + testCases := []struct { + name string + denom string + packet channeltypes.Packet + expProviderDenom string + }{ + { + name: "returns base denom with destination port and channel as prefix", + denom: "stake", + packet: channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + expProviderDenom: "dstPort/dstChannel/stake", + }, + { + name: "returns base denom if the prefix denom corresponds to the packet's port and channel source", + denom: "srcPort/srcChannel/stake", + packet: channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + expProviderDenom: "stake", + }, + { + name: "returns prefixed denom updated with packet's port and channel destination as prefix", + denom: "dstPort/dstChannel/stake", + packet: channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + expProviderDenom: "dstPort/dstChannel/dstPort/dstChannel/stake", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res := provider.GetProviderDenom( + tc.denom, + tc.packet, + ) + + require.Equal(t, tc.expProviderDenom, res) + }) + } +} diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index 5b2e6025ef..45af3eb262 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -1,17 +1,30 @@ package keeper import ( + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + abci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) -// EndBlockRD executes EndBlock logic for the Reward Distribution sub-protocol. -// Reward Distribution follows a simple model: send tokens to the ConsumerRewardsPool, -// from where they sent to the fee collector address -func (k Keeper) EndBlockRD(ctx sdk.Context) { - // transfers all whitelisted consumer rewards to the fee collector address - k.TransferRewardsToFeeCollector(ctx) +// BeginBlockRD executes BeginBlock logic for the Reward Distribution sub-protocol. +func (k Keeper) BeginBlockRD(ctx sdk.Context, req abci.RequestBeginBlock) { + + // determine the total power signing the block + var previousTotalPower int64 + for _, voteInfo := range req.LastCommitInfo.GetVotes() { + previousTotalPower += voteInfo.Validator.Power + } + + // TODO this is Tendermint-dependent + // ref https://github.com/cosmos/cosmos-sdk/issues/3095 + if ctx.BlockHeight() > 1 { + k.AllocateTokens(ctx, previousTotalPower, req.LastCommitInfo.GetVotes()) + } } func (k Keeper) GetConsumerRewardsPoolAddressStr(ctx sdk.Context) string { @@ -57,32 +70,200 @@ func (k Keeper) GetAllConsumerRewardDenoms(ctx sdk.Context) (consumerRewardDenom return consumerRewardDenoms } -// TransferRewardsToFeeCollector transfers all consumer rewards to the fee collector address -func (k Keeper) TransferRewardsToFeeCollector(ctx sdk.Context) { - // 1. Get the denom whitelist from the store - denoms := k.GetAllConsumerRewardDenoms(ctx) +// AllocateTokens performs rewards distribution to the community pool and validators +// based on the Partial Set Security distribution specification. +func (k Keeper) AllocateTokens(ctx sdk.Context, totalPreviousPower int64, bondedVotes []abci.VoteInfo) { + // return if there is no coins in the consumer rewards pool + if k.GetConsumerRewardsPool(ctx).IsZero() { + return + } + + // Iterate over all registered consumer chains + for _, consumer := range k.GetAllConsumerChains(ctx) { + // transfer the consumer rewards to the distribution module account + // note that the rewards transferred are only consumer whitelisted denoms + rewardsCollected, err := k.TransferConsumerRewardsToDistributionModule(ctx, consumer.ChainId) + if err != nil { + k.Logger(ctx).Error( + "fail to transfer rewards to distribution module for chain %s: %s", + consumer.ChainId, + err, + ) + continue + } + + if rewardsCollected.IsZero() { + continue + } + + rewardsCollectedDec := sdk.NewDecCoinsFromCoins(rewardsCollected...) + + // temporary workaround to keep CanWithdrawInvariant happy + // general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634 + feePool := k.distributionKeeper.GetFeePool(ctx) + if k.ComputeConsumerTotalVotingPower(ctx, consumer.ChainId, bondedVotes) == 0 { + feePool.CommunityPool = feePool.CommunityPool.Add(rewardsCollectedDec...) + k.distributionKeeper.SetFeePool(ctx, feePool) + return + } + + // Calculate the reward allocations + remaining := rewardsCollectedDec + communityTax := k.distributionKeeper.GetCommunityTax(ctx) + voteMultiplier := math.LegacyOneDec().Sub(communityTax) + feeMultiplier := rewardsCollectedDec.MulDecTruncate(voteMultiplier) - // 2. Iterate over the whitelist - for _, denom := range denoms { - // 3. For each denom, retrieve the balance from the consumer rewards pool - balance := k.bankKeeper.GetBalance( + // allocate tokens to consumer validators + feeAllocated := k.AllocateTokensToConsumerValidators( ctx, - k.accountKeeper.GetModuleAccount(ctx, types.ConsumerRewardsPool).GetAddress(), - denom, + consumer.ChainId, + bondedVotes, + feeMultiplier, ) + remaining = remaining.Sub(feeAllocated) - // if the balance is not zero, - if !balance.IsZero() { - // 4. Transfer the balance to the fee collector address - err := k.bankKeeper.SendCoinsFromModuleToModule( - ctx, - types.ConsumerRewardsPool, - k.feeCollectorName, - sdk.NewCoins(balance), - ) - if err != nil { - k.Logger(ctx).Error("cannot sent consumer rewards to fee collector:", "reward", balance.String()) - } - } + // allocate community funding + feePool.CommunityPool = feePool.CommunityPool.Add(remaining...) + k.distributionKeeper.SetFeePool(ctx, feePool) } } + +// TODO: allocate tokens to validators that opted-in and for long enough e.g. 1000 blocks +// once the opt-in logic is integrated QueueVSCPackets() +// +// AllocateTokensToConsumerValidators allocates the given tokens from the +// from consumer rewards pool to validator according to their voting power +func (k Keeper) AllocateTokensToConsumerValidators( + ctx sdk.Context, + chainID string, + bondedVotes []abci.VoteInfo, + tokens sdk.DecCoins, +) (totalReward sdk.DecCoins) { + // return early if the tokens are empty + if tokens.Empty() { + return totalReward + } + + // get the consumer total voting power from the votes + totalPower := k.ComputeConsumerTotalVotingPower(ctx, chainID, bondedVotes) + if totalPower == 0 { + return totalReward + } + + for _, vote := range bondedVotes { + // TODO: should check if validator IsOptIn or continue here + consAddr := sdk.ConsAddress(vote.Validator.Address) + + powerFraction := math.LegacyNewDec(vote.Validator.Power).QuoTruncate(math.LegacyNewDec(totalPower)) + tokensFraction := tokens.MulDecTruncate(powerFraction) + + k.distributionKeeper.AllocateTokensToValidator( + ctx, + k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr), + tokensFraction, + ) + totalReward = totalReward.Add(tokensFraction...) + } + + return totalReward +} + +// TransferConsumerRewardsToDistributionModule transfers the collected rewards of the given consumer chain +// from the consumer rewards pool module account to a the distribution module +func (k Keeper) TransferConsumerRewardsToDistributionModule( + ctx sdk.Context, + chainID string, +) (sdk.Coins, error) { + // Get coins of the consumer rewards allocation + allocation := k.GetConsumerRewardsAllocation(ctx, chainID) + + if allocation.Rewards.IsZero() { + return sdk.Coins{}, nil + } + + // Truncate coin rewards + rewardsToSend, _ := allocation.Rewards.TruncateDecimal() + + // NOTE the consumer rewards allocation isn't a module account, however its coins + // are held in the consumer reward pool module account. Thus the consumer + // rewards allocation must be reduced separately from the SendCoinsFromModuleToAccount call. + + // Update consumer rewards allocation with the remaining decimal coins + allocation.Rewards = allocation.Rewards.Sub(sdk.NewDecCoinsFromCoins(rewardsToSend...)) + + // Send coins to distribution module account + err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ConsumerRewardsPool, distrtypes.ModuleName, rewardsToSend) + if err != nil { + return sdk.Coins{}, err + } + + k.SetConsumerRewardsAllocation(ctx, chainID, allocation) + return rewardsToSend, nil +} + +// consumer reward pools getter and setter + +// GetConsumerRewardsAllocation returns the consumer rewards allocation for the given chain ID +func (k Keeper) GetConsumerRewardsAllocation(ctx sdk.Context, chainID string) (pool types.ConsumerRewardsAllocation) { + store := ctx.KVStore(k.storeKey) + b := store.Get(types.ConsumerRewardsAllocationKey(chainID)) + k.cdc.MustUnmarshal(b, &pool) + return +} + +// SetConsumerRewardsAllocation sets the consumer rewards allocation for the given chain ID +func (k Keeper) SetConsumerRewardsAllocation(ctx sdk.Context, chainID string, pool types.ConsumerRewardsAllocation) { + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshal(&pool) + store.Set(types.ConsumerRewardsAllocationKey(chainID), b) +} + +// GetConsumerRewardsPool returns the balance +// of the consumer rewards pool module account +func (k Keeper) GetConsumerRewardsPool(ctx sdk.Context) sdk.Coins { + return k.bankKeeper.GetAllBalances( + ctx, + k.accountKeeper.GetModuleAccount(ctx, types.ConsumerRewardsPool).GetAddress(), + ) +} + +// ComputeConsumerTotalVotingPower returns the total voting power for a given consumer chain +// by summing its opted-in validators votes +func (k Keeper) ComputeConsumerTotalVotingPower(ctx sdk.Context, chainID string, votes []abci.VoteInfo) int64 { + // TODO: create a optedIn set from the OptedIn validators + // and sum their validator power + var totalPower int64 + + // sum the opted-in validators set voting powers + for _, vote := range votes { + // TODO: check that val is in the optedIn set + + totalPower += vote.Validator.Power + } + + return totalPower +} + +// IdentifyConsumerChainIDFromIBCPacket checks if the packet destination matches a registered consumer chain. +// If so, it returns the consumer chain ID, otherwise an error. +func (k Keeper) IdentifyConsumerChainIDFromIBCPacket(ctx sdk.Context, packet channeltypes.Packet) (string, error) { + channel, ok := k.channelKeeper.GetChannel(ctx, packet.DestinationPort, packet.DestinationChannel) + if !ok { + return "", errorsmod.Wrapf(channeltypes.ErrChannelNotFound, "channel not found for channel ID: %s", packet.DestinationChannel) + } + if len(channel.ConnectionHops) != 1 { + return "", errorsmod.Wrap(channeltypes.ErrTooManyConnectionHops, "must have direct connection to consumer chain") + } + connectionID := channel.ConnectionHops[0] + _, tmClient, err := k.getUnderlyingClient(ctx, connectionID) + if err != nil { + return "", err + } + + chainID := tmClient.ChainId + if _, ok := k.GetChainToChannel(ctx, chainID); !ok { + return "", errorsmod.Wrapf(types.ErrUnknownConsumerChannelId, "no CCV channel found for chain with ID: %s", chainID) + } + + return chainID, nil +} diff --git a/x/ccv/provider/keeper/distribution_test.go b/x/ccv/provider/keeper/distribution_test.go new file mode 100644 index 0000000000..b8883177b1 --- /dev/null +++ b/x/ccv/provider/keeper/distribution_test.go @@ -0,0 +1,238 @@ +package keeper_test + +import ( + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + tmtypes "github.com/cometbft/cometbft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func TestComputeConsumerTotalVotingPower(t *testing.T) { + keeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + createVal := func(power int64) tmtypes.Validator { + signer := tmtypes.NewMockPV() + val := tmtypes.NewValidator(signer.PrivKey.PubKey(), power) + return *val + } + + chainID := "consumer" + validatorsVotes := make([]abci.VoteInfo, 5) + + expTotalPower := int64(0) + + // create validators, opt them in and use them + // to create block votes + for i := 0; i < 5; i++ { + val := createVal(int64(i)) + keeper.SetOptedIn( + ctx, + chainID, + types.NewProviderConsAddress(sdk.ConsAddress(val.Address)), + 0, + ) + + validatorsVotes = append( + validatorsVotes, + abci.VoteInfo{ + Validator: abci.Validator{ + Address: val.Address, + Power: val.VotingPower, + }, + }, + ) + + expTotalPower += val.VotingPower + } + + res := keeper.ComputeConsumerTotalVotingPower( + ctx, + chainID, + validatorsVotes, + ) + + require.Equal(t, expTotalPower, res) +} + +func TestIdentifyConsumerChainIDFromIBCPacket(t *testing.T) { + + var ( + chainID = "consumer" + ccvChannel = "channel-0" + ) + + testCases := []struct { + name string + packet channeltypes.Packet + expectedCalls func(sdk.Context, testkeeper.MockedKeepers, channeltypes.Packet) []*gomock.Call + expCCVChannel bool + expErr bool + }{ + { + "channel not found", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ).Return(channeltypes.Channel{}, false).Times(1), + } + }, + false, + true, + }, + { + "connection hops can't be greater than 1", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ).Return(channeltypes.Channel{ConnectionHops: []string{"conn1", "conn2"}}, true).Times(1), + } + }, + false, + true, + }, + { + "underlying client isn't found", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ).Return(channeltypes.Channel{ConnectionHops: []string{"connectionID"}}, true).Times(1), + mocks.MockConnectionKeeper.EXPECT().GetConnection(ctx, "connectionID").Return( + conntypes.ConnectionEnd{ClientId: "clientID"}, true, + ).Times(1), + mocks.MockClientKeeper.EXPECT().GetClientState(ctx, "clientID").Return( + &ibctmtypes.ClientState{ChainId: ""}, false, + ).Times(1), + } + }, + false, + true, + }, + { + "no CCV channel registered", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ).Return(channeltypes.Channel{ConnectionHops: []string{"connectionID"}}, true).Times(1), + mocks.MockConnectionKeeper.EXPECT().GetConnection(ctx, "connectionID").Return( + conntypes.ConnectionEnd{ClientId: "clientID"}, true, + ).Times(1), + mocks.MockClientKeeper.EXPECT().GetClientState(ctx, "clientID").Return( + &ibctmtypes.ClientState{ChainId: chainID}, true, + ).Times(1), + } + }, + false, + true, + }, + { + "consumer chain identified", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ), + } + }, + false, + true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + keeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + tc.expectedCalls(ctx, mocks, tc.packet) + _, err := keeper.IdentifyConsumerChainIDFromIBCPacket( + ctx, + tc.packet, + ) + + if tc.expCCVChannel { + keeper.SetChainToChannel(ctx, chainID, ccvChannel) + } + + if !tc.expErr { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index f34b92bb07..1fe4ae08bb 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -144,6 +144,8 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { am.keeper.BeginBlockCCR(ctx) // Check for replenishing slash meter before any slash packets are processed for this block am.keeper.BeginBlockCIS(ctx) + // BeginBlock logic need for the Reward Distribution sub-protocol + am.keeper.BeginBlockRD(ctx, req) } // EndBlock implements the AppModule interface @@ -155,8 +157,6 @@ func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.V am.keeper.EndBlockCCR(ctx) // EndBlock logic needed for the Validator Set Update sub-protocol am.keeper.EndBlockVSU(ctx) - // EndBlock logic need for the Reward Distribution sub-protocol - am.keeper.EndBlockRD(ctx) return []abci.ValidatorUpdate{} } diff --git a/x/ccv/provider/types/consumer.go b/x/ccv/provider/types/consumer.go index 4c43bd58e7..2a89859a36 100644 --- a/x/ccv/provider/types/consumer.go +++ b/x/ccv/provider/types/consumer.go @@ -1,6 +1,7 @@ package types import ( + sdk "github.com/cosmos/cosmos-sdk/types" ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) @@ -25,3 +26,10 @@ func NewConsumerStates( SlashDowntimeAck: slashDowntimeAck, } } + +// zero consumer rewards allocation +func InitialConsumerRewardsAllocation() ConsumerRewardsAllocation { + return ConsumerRewardsAllocation{ + Rewards: sdk.DecCoins{}, + } +} diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 20824454cb..ed86ea0804 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -160,6 +160,10 @@ const ( // are about to be opted out ToBeOptedOutBytePrefix + // ConsumerRewardsAllocationBytePrefix is the byte prefix used when storing for each consumer the rewards + // it allocated to the consumer rewards pool + ConsumerRewardsAllocationBytePrefix + // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go ) @@ -555,6 +559,11 @@ func ToBeOptedOutKey(chainID string, providerAddr ProviderConsAddress) []byte { return append(prefix, providerAddr.ToSdkConsAddr().Bytes()...) } +// ConsumerModuleAccount returns the module account byte prefix for a consumer chain +func ConsumerRewardsAllocationKey(chainID string) []byte { + return append([]byte{ConsumerRewardsAllocationBytePrefix}, []byte(chainID)...) +} + // // End of generic helpers section // diff --git a/x/ccv/provider/types/keys_test.go b/x/ccv/provider/types/keys_test.go index 62ca2b9fef..9d797011b6 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -60,6 +60,7 @@ func getAllKeyPrefixes() []byte { providertypes.OptedInBytePrefix, providertypes.ToBeOptedInBytePrefix, providertypes.ToBeOptedOutBytePrefix, + providertypes.ConsumerRewardsAllocationBytePrefix, } } diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index fd9d63bc7e..20733b4fa0 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -6,7 +6,9 @@ package types import ( fmt "fmt" crypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types2 "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" types1 "github.com/cosmos/cosmos-sdk/x/evidence/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" @@ -1390,6 +1392,51 @@ func (m *ConsumerAddrsToPrune) GetConsumerAddrs() *AddressList { return nil } +// ConsumerRewardsAllocation is used to serialize the allocation of consumer chain rewards +type ConsumerRewardsAllocation struct { + Rewards github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=rewards,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"rewards"` +} + +func (m *ConsumerRewardsAllocation) Reset() { *m = ConsumerRewardsAllocation{} } +func (m *ConsumerRewardsAllocation) String() string { return proto.CompactTextString(m) } +func (*ConsumerRewardsAllocation) ProtoMessage() {} +func (*ConsumerRewardsAllocation) Descriptor() ([]byte, []int) { + return fileDescriptor_f22ec409a72b7b72, []int{22} +} +func (m *ConsumerRewardsAllocation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsumerRewardsAllocation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsumerRewardsAllocation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConsumerRewardsAllocation) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsumerRewardsAllocation.Merge(m, src) +} +func (m *ConsumerRewardsAllocation) XXX_Size() int { + return m.Size() +} +func (m *ConsumerRewardsAllocation) XXX_DiscardUnknown() { + xxx_messageInfo_ConsumerRewardsAllocation.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsumerRewardsAllocation proto.InternalMessageInfo + +func (m *ConsumerRewardsAllocation) GetRewards() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.Rewards + } + return nil +} + func init() { proto.RegisterType((*ConsumerAdditionProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerAdditionProposal") proto.RegisterType((*ConsumerRemovalProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerRemovalProposal") @@ -1413,6 +1460,7 @@ func init() { proto.RegisterType((*ValidatorConsumerPubKey)(nil), "interchain_security.ccv.provider.v1.ValidatorConsumerPubKey") proto.RegisterType((*ValidatorByConsumerAddr)(nil), "interchain_security.ccv.provider.v1.ValidatorByConsumerAddr") proto.RegisterType((*ConsumerAddrsToPrune)(nil), "interchain_security.ccv.provider.v1.ConsumerAddrsToPrune") + proto.RegisterType((*ConsumerRewardsAllocation)(nil), "interchain_security.ccv.provider.v1.ConsumerRewardsAllocation") } func init() { @@ -1420,114 +1468,119 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1712 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4f, 0x73, 0xdb, 0xc6, - 0x15, 0x17, 0x44, 0x4a, 0x16, 0x1f, 0xf5, 0xcf, 0x90, 0x12, 0x43, 0xae, 0x4a, 0xd1, 0x48, 0x93, - 0xaa, 0x93, 0x09, 0x58, 0x29, 0xed, 0x4c, 0xc6, 0xd3, 0x4c, 0x46, 0xa2, 0x9c, 0x58, 0x56, 0x63, - 0x2b, 0x90, 0x2a, 0x4f, 0xdb, 0x03, 0x66, 0xb9, 0x58, 0x93, 0x3b, 0x02, 0xb1, 0xf0, 0xee, 0x02, - 0x0e, 0x2f, 0x3d, 0xf7, 0x98, 0xde, 0x32, 0xbd, 0x34, 0xed, 0x17, 0xe8, 0xb9, 0xdf, 0x20, 0xc7, - 0x1c, 0x7b, 0x4a, 0x3b, 0xf2, 0xb1, 0xd7, 0x7e, 0x80, 0xce, 0x2e, 0xfe, 0x92, 0x12, 0x5d, 0x7a, - 0xdc, 0xde, 0x80, 0xb7, 0xef, 0xfd, 0xde, 0xdb, 0xf7, 0xe7, 0xf7, 0x40, 0xc2, 0x3e, 0x0d, 0x25, - 0xe1, 0x78, 0x80, 0x68, 0xe8, 0x09, 0x82, 0x63, 0x4e, 0xe5, 0xa8, 0x83, 0x71, 0xd2, 0x89, 0x38, - 0x4b, 0xa8, 0x4f, 0x78, 0x27, 0xd9, 0x2b, 0x9e, 0x9d, 0x88, 0x33, 0xc9, 0xcc, 0x77, 0x6e, 0xb0, - 0x71, 0x30, 0x4e, 0x9c, 0x42, 0x2f, 0xd9, 0xbb, 0xfb, 0xee, 0x34, 0xe0, 0x64, 0xaf, 0xf3, 0x82, - 0x72, 0x92, 0x62, 0xdd, 0xdd, 0xec, 0xb3, 0x3e, 0xd3, 0x8f, 0x1d, 0xf5, 0x94, 0x49, 0x77, 0xfa, - 0x8c, 0xf5, 0x03, 0xd2, 0xd1, 0x6f, 0xbd, 0xf8, 0x59, 0x47, 0xd2, 0x21, 0x11, 0x12, 0x0d, 0xa3, - 0x4c, 0xa1, 0x35, 0xa9, 0xe0, 0xc7, 0x1c, 0x49, 0xca, 0xc2, 0x1c, 0x80, 0xf6, 0x70, 0x07, 0x33, - 0x4e, 0x3a, 0x38, 0xa0, 0x24, 0x94, 0xca, 0x6b, 0xfa, 0x94, 0x29, 0x74, 0x94, 0x42, 0x40, 0xfb, - 0x03, 0x99, 0x8a, 0x45, 0x47, 0x92, 0xd0, 0x27, 0x7c, 0x48, 0x53, 0xe5, 0xf2, 0x2d, 0x33, 0xd8, - 0xae, 0x9c, 0x63, 0x3e, 0x8a, 0x24, 0xeb, 0x5c, 0x92, 0x91, 0xc8, 0x4e, 0xdf, 0xc3, 0x4c, 0x0c, - 0x99, 0xe8, 0x10, 0x75, 0xff, 0x10, 0x93, 0x4e, 0xb2, 0xd7, 0x23, 0x12, 0xed, 0x15, 0x82, 0x3c, - 0xee, 0x4c, 0xaf, 0x87, 0x44, 0xa9, 0x83, 0x19, 0xcd, 0xe2, 0xb6, 0xff, 0xbd, 0x08, 0x56, 0x97, - 0x85, 0x22, 0x1e, 0x12, 0x7e, 0xe0, 0xfb, 0x54, 0x5d, 0xe9, 0x94, 0xb3, 0x88, 0x09, 0x14, 0x98, - 0x9b, 0xb0, 0x20, 0xa9, 0x0c, 0x88, 0x65, 0xb4, 0x8d, 0xdd, 0x86, 0x9b, 0xbe, 0x98, 0x6d, 0x68, - 0xfa, 0x44, 0x60, 0x4e, 0x23, 0xa5, 0x6c, 0xcd, 0xeb, 0xb3, 0xaa, 0xc8, 0xdc, 0x82, 0xa5, 0xb4, - 0x0e, 0xd4, 0xb7, 0x6a, 0xfa, 0xf8, 0x96, 0x7e, 0x3f, 0xf6, 0xcd, 0xcf, 0x60, 0x95, 0x86, 0x54, - 0x52, 0x14, 0x78, 0x03, 0xa2, 0xb2, 0x61, 0xd5, 0xdb, 0xc6, 0x6e, 0x73, 0xff, 0xae, 0x43, 0x7b, - 0xd8, 0x51, 0x09, 0x74, 0xb2, 0xb4, 0x25, 0x7b, 0xce, 0x43, 0xad, 0x71, 0x58, 0xff, 0xf6, 0xfb, - 0x9d, 0x39, 0x77, 0x25, 0xb3, 0x4b, 0x85, 0xe6, 0x3d, 0x58, 0xee, 0x93, 0x90, 0x08, 0x2a, 0xbc, - 0x01, 0x12, 0x03, 0x6b, 0xa1, 0x6d, 0xec, 0x2e, 0xbb, 0xcd, 0x4c, 0xf6, 0x10, 0x89, 0x81, 0xb9, - 0x03, 0xcd, 0x1e, 0x0d, 0x11, 0x1f, 0xa5, 0x1a, 0x8b, 0x5a, 0x03, 0x52, 0x91, 0x56, 0xe8, 0x02, - 0x88, 0x08, 0xbd, 0x08, 0x3d, 0x55, 0x6d, 0xeb, 0x56, 0x16, 0x48, 0x5a, 0x69, 0x27, 0xaf, 0xb4, - 0x73, 0x9e, 0xb7, 0xc2, 0xe1, 0x92, 0x0a, 0xe4, 0xab, 0x7f, 0xec, 0x18, 0x6e, 0x43, 0xdb, 0xa9, - 0x13, 0xf3, 0x31, 0xac, 0xc7, 0x61, 0x8f, 0x85, 0x3e, 0x0d, 0xfb, 0x5e, 0x44, 0x38, 0x65, 0xbe, - 0xb5, 0xa4, 0xa1, 0xb6, 0xae, 0x41, 0x1d, 0x65, 0x4d, 0x93, 0x22, 0x7d, 0xad, 0x90, 0xd6, 0x0a, - 0xe3, 0x53, 0x6d, 0x6b, 0x7e, 0x01, 0x26, 0xc6, 0x89, 0x0e, 0x89, 0xc5, 0x32, 0x47, 0x6c, 0xcc, - 0x8e, 0xb8, 0x8e, 0x71, 0x72, 0x9e, 0x5a, 0x67, 0x90, 0xbf, 0x85, 0x3b, 0x92, 0xa3, 0x50, 0x3c, - 0x23, 0x7c, 0x12, 0x17, 0x66, 0xc7, 0x7d, 0x2b, 0xc7, 0x18, 0x07, 0x7f, 0x08, 0x6d, 0x9c, 0x35, - 0x90, 0xc7, 0x89, 0x4f, 0x85, 0xe4, 0xb4, 0x17, 0x2b, 0x5b, 0xef, 0x19, 0x47, 0x58, 0xf7, 0x48, - 0x53, 0x37, 0x41, 0x2b, 0xd7, 0x73, 0xc7, 0xd4, 0x3e, 0xcd, 0xb4, 0xcc, 0x27, 0xf0, 0xa3, 0x5e, - 0xc0, 0xf0, 0xa5, 0x50, 0xc1, 0x79, 0x63, 0x48, 0xda, 0xf5, 0x90, 0x0a, 0xa1, 0xd0, 0x96, 0xdb, - 0xc6, 0x6e, 0xcd, 0xbd, 0x97, 0xea, 0x9e, 0x12, 0x7e, 0x54, 0xd1, 0x3c, 0xaf, 0x28, 0x9a, 0x1f, - 0x80, 0x39, 0xa0, 0x42, 0x32, 0x4e, 0x31, 0x0a, 0x3c, 0x12, 0x4a, 0x4e, 0x89, 0xb0, 0x56, 0xb4, - 0xf9, 0xed, 0xf2, 0xe4, 0x41, 0x7a, 0x60, 0x3e, 0x82, 0x7b, 0x53, 0x9d, 0x7a, 0x78, 0x80, 0xc2, - 0x90, 0x04, 0xd6, 0xaa, 0xbe, 0xca, 0x8e, 0x3f, 0xc5, 0x67, 0x37, 0x55, 0x33, 0x37, 0x60, 0x41, - 0xb2, 0xc8, 0x7b, 0x6c, 0xad, 0xb5, 0x8d, 0xdd, 0x15, 0xb7, 0x2e, 0x59, 0xf4, 0xf8, 0xfe, 0xd2, - 0xef, 0xbf, 0xd9, 0x99, 0xfb, 0xfa, 0x9b, 0x9d, 0x39, 0xfb, 0xaf, 0x06, 0xdc, 0xe9, 0x16, 0xd9, - 0x18, 0xb2, 0x04, 0x05, 0xff, 0xcf, 0xa9, 0x3b, 0x80, 0x86, 0x50, 0xe1, 0xe8, 0x3e, 0xaf, 0xbf, - 0x46, 0x9f, 0x2f, 0x29, 0x33, 0x75, 0x60, 0xff, 0xc9, 0x80, 0xcd, 0x07, 0xcf, 0x63, 0x9a, 0x30, - 0x8c, 0xfe, 0x27, 0x24, 0x71, 0x02, 0x2b, 0xa4, 0x82, 0x27, 0xac, 0x5a, 0xbb, 0xb6, 0xdb, 0xdc, - 0x7f, 0xd7, 0x49, 0x19, 0xcb, 0x29, 0x88, 0x2c, 0x63, 0x2d, 0xa7, 0xea, 0xdd, 0x1d, 0xb7, 0xbd, - 0x3f, 0x6f, 0x19, 0xf6, 0x5f, 0x0c, 0xb8, 0xab, 0xd2, 0xdf, 0x27, 0x2e, 0x79, 0x81, 0xb8, 0x7f, - 0x44, 0x42, 0x36, 0x14, 0x6f, 0x1c, 0xa7, 0x0d, 0x2b, 0xbe, 0x46, 0xf2, 0x24, 0xf3, 0x90, 0xef, - 0xeb, 0x38, 0xb5, 0x8e, 0x12, 0x9e, 0xb3, 0x03, 0xdf, 0x37, 0x77, 0x61, 0xbd, 0xd4, 0xe1, 0xaa, - 0x9e, 0x2a, 0xcd, 0x4a, 0x6d, 0x35, 0x57, 0xd3, 0x55, 0x26, 0xf6, 0xbf, 0x0c, 0x58, 0xff, 0x2c, - 0x60, 0x3d, 0x14, 0x9c, 0x05, 0x48, 0x0c, 0x54, 0xeb, 0x8d, 0x54, 0x79, 0x38, 0xc9, 0x66, 0x5e, - 0x87, 0x37, 0x73, 0x79, 0x94, 0x99, 0x66, 0xa1, 0x4f, 0xe0, 0x76, 0x31, 0x85, 0x45, 0x17, 0xe8, - 0xdb, 0x1c, 0x6e, 0x5c, 0x7d, 0xbf, 0xb3, 0x96, 0x37, 0x5b, 0x57, 0x77, 0xc4, 0x91, 0xbb, 0x86, - 0xc7, 0x04, 0xbe, 0xd9, 0x82, 0x26, 0xed, 0x61, 0x4f, 0x90, 0xe7, 0x5e, 0x18, 0x0f, 0x75, 0x03, - 0xd5, 0xdd, 0x06, 0xed, 0xe1, 0x33, 0xf2, 0xfc, 0x71, 0x3c, 0x34, 0x3f, 0x84, 0xb7, 0xf3, 0x6d, - 0xeb, 0x25, 0x28, 0xf0, 0x94, 0xbd, 0x4a, 0x07, 0xd7, 0xfd, 0xb4, 0xec, 0x6e, 0xe4, 0xa7, 0x17, - 0x28, 0x50, 0xce, 0x0e, 0x7c, 0x9f, 0xdb, 0x7f, 0x5b, 0x80, 0xc5, 0x53, 0xc4, 0xd1, 0x50, 0x98, - 0xe7, 0xb0, 0x26, 0xc9, 0x30, 0x0a, 0x90, 0x24, 0x5e, 0xca, 0xf0, 0xd9, 0x4d, 0xdf, 0xd7, 0xcc, - 0x5f, 0xdd, 0x8c, 0x4e, 0x65, 0x17, 0x26, 0x7b, 0x4e, 0x57, 0x4b, 0xcf, 0x24, 0x92, 0xc4, 0x5d, - 0xcd, 0x31, 0x52, 0xa1, 0xf9, 0x11, 0x58, 0x92, 0xc7, 0x42, 0x96, 0xdc, 0x5b, 0x92, 0x4e, 0x5a, - 0xcb, 0xb7, 0xf3, 0xf3, 0x94, 0xae, 0x0a, 0xb2, 0xb9, 0x99, 0x66, 0x6b, 0x6f, 0x42, 0xb3, 0x67, - 0xb0, 0xa1, 0x76, 0xd4, 0x24, 0x66, 0x7d, 0x76, 0xcc, 0xdb, 0xca, 0x7e, 0x1c, 0xf4, 0x0b, 0x30, - 0x13, 0x81, 0x27, 0x31, 0x17, 0x5e, 0x23, 0xce, 0x44, 0xe0, 0x71, 0x48, 0x1f, 0xb6, 0x85, 0x6a, - 0x3e, 0x6f, 0x48, 0xa4, 0x26, 0xed, 0x28, 0x20, 0x21, 0x15, 0x83, 0x1c, 0x7c, 0x71, 0x76, 0xf0, - 0x2d, 0x0d, 0xf4, 0xb9, 0xc2, 0x71, 0x73, 0x98, 0xcc, 0x4b, 0x17, 0x5a, 0x37, 0x7b, 0x29, 0x0a, - 0x74, 0x4b, 0x17, 0xe8, 0x07, 0x37, 0x40, 0x14, 0x55, 0x12, 0xf0, 0x5e, 0x65, 0xb9, 0xa8, 0xa9, - 0xf6, 0xf4, 0x40, 0x79, 0x9c, 0xf4, 0x15, 0x03, 0xa3, 0x74, 0xcf, 0x10, 0x52, 0x2c, 0xc8, 0x8c, - 0x3d, 0xd4, 0xf7, 0x4e, 0xc1, 0x1c, 0x5d, 0x46, 0xc3, 0xec, 0x2b, 0xc2, 0x2e, 0x77, 0x50, 0xc1, - 0x11, 0x6e, 0x05, 0xeb, 0x53, 0x42, 0x1e, 0xd5, 0x97, 0x96, 0xd6, 0x1b, 0xf6, 0x4f, 0xa0, 0xa1, - 0x47, 0xf4, 0x00, 0x5f, 0x0a, 0x73, 0x1b, 0x1a, 0xaa, 0xd7, 0x89, 0x10, 0x44, 0x58, 0x86, 0x9e, - 0xec, 0x52, 0x60, 0x4b, 0xd8, 0x9a, 0xf6, 0x0d, 0x25, 0xcc, 0xa7, 0x70, 0x2b, 0x22, 0x7a, 0xc1, - 0x6b, 0xc3, 0xe6, 0xfe, 0xc7, 0xce, 0x0c, 0x9f, 0xb3, 0xce, 0x34, 0x40, 0x37, 0x47, 0xb3, 0x79, - 0xf9, 0xe5, 0x36, 0xb1, 0x42, 0x84, 0x79, 0x31, 0xe9, 0xf4, 0x17, 0xaf, 0xe5, 0x74, 0x02, 0xaf, - 0xf4, 0xf9, 0x3e, 0x34, 0x0f, 0xd2, 0x6b, 0xff, 0x92, 0x0a, 0x79, 0x3d, 0x2d, 0xcb, 0xd5, 0xb4, - 0x3c, 0x82, 0xd5, 0x6c, 0x1d, 0x9e, 0x33, 0x4d, 0x33, 0xe6, 0x0f, 0x01, 0xb2, 0x3d, 0xaa, 0xe8, - 0x29, 0x25, 0xe2, 0x46, 0x26, 0x39, 0xf6, 0xc7, 0x36, 0xd8, 0xfc, 0xd8, 0x06, 0xb3, 0x5d, 0x58, - 0xbb, 0x10, 0xf8, 0x57, 0xf9, 0xb7, 0xd2, 0x93, 0x48, 0x98, 0x6f, 0xc1, 0xa2, 0x9a, 0x8c, 0x0c, - 0xa8, 0xee, 0x2e, 0x24, 0x02, 0x1f, 0x6b, 0x2e, 0x2e, 0xbf, 0xc7, 0x58, 0xe4, 0x51, 0x5f, 0x58, - 0xf3, 0xed, 0xda, 0x6e, 0xdd, 0x5d, 0x8d, 0x4b, 0xf3, 0x63, 0x5f, 0xd8, 0xbf, 0x86, 0x66, 0x05, - 0xd0, 0x5c, 0x85, 0xf9, 0x02, 0x6b, 0x9e, 0xfa, 0xe6, 0x7d, 0xd8, 0x2a, 0x81, 0xc6, 0xc9, 0x35, - 0x45, 0x6c, 0xb8, 0x77, 0x0a, 0x85, 0x31, 0x7e, 0x15, 0xf6, 0x13, 0xd8, 0x3c, 0x2e, 0x47, 0xb9, - 0xa0, 0xee, 0xb1, 0x1b, 0x1a, 0xe3, 0x3b, 0x7a, 0x1b, 0x1a, 0xc5, 0x8f, 0x0e, 0x7d, 0xfb, 0xba, - 0x5b, 0x0a, 0xec, 0x21, 0xac, 0x5f, 0x08, 0x7c, 0x46, 0x42, 0xbf, 0x04, 0x9b, 0x92, 0x80, 0xc3, - 0x49, 0xa0, 0x99, 0x3f, 0x6a, 0x4b, 0x77, 0x0c, 0xb6, 0x2e, 0x50, 0x40, 0x7d, 0x24, 0x19, 0x3f, - 0x23, 0x32, 0x5d, 0xab, 0xa7, 0x08, 0x5f, 0x12, 0x29, 0x4c, 0x17, 0xea, 0x01, 0x15, 0x32, 0xeb, - 0xac, 0x8f, 0xa6, 0x76, 0x56, 0xb2, 0xe7, 0x4c, 0x03, 0x39, 0x42, 0x12, 0x65, 0x13, 0xa9, 0xb1, - 0xec, 0x1f, 0xc3, 0xc6, 0xe7, 0x48, 0xc6, 0x9c, 0xf8, 0x63, 0x35, 0x5e, 0x87, 0x9a, 0xaa, 0x9f, - 0xa1, 0xeb, 0xa7, 0x1e, 0xd5, 0x96, 0xb7, 0x1e, 0x7c, 0x19, 0x31, 0x2e, 0x89, 0x7f, 0x2d, 0x23, - 0xaf, 0x48, 0xef, 0x25, 0x6c, 0xa8, 0x64, 0x09, 0x12, 0xfa, 0x5e, 0x71, 0xcf, 0xb4, 0x8e, 0xcd, - 0xfd, 0x9f, 0xcf, 0x34, 0x1d, 0x93, 0xee, 0xb2, 0x0b, 0xdc, 0x4e, 0x26, 0xe4, 0xc2, 0xfe, 0x83, - 0x01, 0xd6, 0x09, 0x19, 0x1d, 0x08, 0x41, 0xfb, 0xe1, 0x90, 0x84, 0x52, 0x31, 0x1b, 0xc2, 0x44, - 0x3d, 0x9a, 0xef, 0xc0, 0x4a, 0xb1, 0x49, 0xf5, 0x02, 0x35, 0xf4, 0x02, 0x5d, 0xce, 0x85, 0x6a, - 0xc0, 0xcc, 0xfb, 0x00, 0x11, 0x27, 0x89, 0x87, 0xbd, 0x4b, 0x32, 0xca, 0xaa, 0xb8, 0x5d, 0x5d, - 0x8c, 0xe9, 0x4f, 0x42, 0xe7, 0x34, 0xee, 0x05, 0x14, 0x9f, 0x90, 0x91, 0xbb, 0xa4, 0xf4, 0xbb, - 0x27, 0x64, 0xa4, 0xbe, 0x74, 0x22, 0xf6, 0x82, 0x70, 0xbd, 0xcd, 0x6a, 0x6e, 0xfa, 0x62, 0xff, - 0xd1, 0x80, 0x3b, 0x45, 0x39, 0xf2, 0x76, 0x3d, 0x8d, 0x7b, 0xca, 0xe2, 0x15, 0x79, 0xbb, 0x16, - 0xed, 0xfc, 0x0d, 0xd1, 0x7e, 0x02, 0xcb, 0xc5, 0x80, 0xa8, 0x78, 0x6b, 0x33, 0xc4, 0xdb, 0xcc, - 0x2d, 0x4e, 0xc8, 0xc8, 0xfe, 0x5d, 0x25, 0xb6, 0xc3, 0x51, 0x85, 0xfb, 0xf8, 0x7f, 0x89, 0xad, - 0x70, 0x5b, 0x8d, 0x0d, 0x57, 0xed, 0xaf, 0x5d, 0xa0, 0x76, 0xfd, 0x02, 0xf6, 0x9f, 0x0d, 0xd8, - 0xac, 0x7a, 0x15, 0xe7, 0xec, 0x94, 0xc7, 0x21, 0x79, 0x95, 0xf7, 0x72, 0xfc, 0xe6, 0xab, 0xe3, - 0xf7, 0x14, 0x56, 0xc7, 0x82, 0x12, 0x59, 0x36, 0x7e, 0x3a, 0x53, 0x8f, 0x55, 0xd8, 0xd5, 0x5d, - 0xa9, 0xde, 0x43, 0x1c, 0x3e, 0xfd, 0xf6, 0xaa, 0x65, 0x7c, 0x77, 0xd5, 0x32, 0xfe, 0x79, 0xd5, - 0x32, 0xbe, 0x7a, 0xd9, 0x9a, 0xfb, 0xee, 0x65, 0x6b, 0xee, 0xef, 0x2f, 0x5b, 0x73, 0xbf, 0xf9, - 0xb8, 0x4f, 0xe5, 0x20, 0xee, 0x39, 0x98, 0x0d, 0x3b, 0xd9, 0xef, 0xfd, 0xd2, 0xd7, 0x07, 0xc5, - 0x9f, 0x21, 0xc9, 0xcf, 0x3a, 0x5f, 0x8e, 0xff, 0xd5, 0x22, 0x47, 0x11, 0x11, 0xbd, 0x45, 0xcd, - 0x0a, 0x1f, 0xfe, 0x27, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x99, 0xdf, 0x57, 0x9b, 0x11, 0x00, 0x00, + // 1787 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x73, 0x1c, 0x47, + 0x15, 0xd7, 0x68, 0x57, 0x1f, 0xfb, 0x56, 0x9f, 0x23, 0x25, 0x1e, 0x19, 0xb1, 0x92, 0x27, 0x24, + 0x08, 0x5c, 0x9e, 0x41, 0x0a, 0x54, 0xb9, 0x5c, 0xa4, 0x52, 0xd2, 0xca, 0x89, 0x65, 0x11, 0x5b, + 0x19, 0x09, 0xb9, 0x80, 0xc3, 0x54, 0x6f, 0x4f, 0x7b, 0xb7, 0x4b, 0xb3, 0xd3, 0xe3, 0xee, 0xde, + 0x71, 0xf6, 0xc2, 0x99, 0x0b, 0x55, 0xe1, 0x96, 0xe2, 0x42, 0xe0, 0x44, 0x71, 0x81, 0x2b, 0xff, + 0x41, 0x8e, 0x39, 0x72, 0x4a, 0x28, 0xfb, 0xc0, 0x81, 0x2b, 0x7f, 0x00, 0xd5, 0x3d, 0x9f, 0xbb, + 0x92, 0xcc, 0xba, 0x42, 0x2e, 0xd2, 0xcc, 0xeb, 0xf7, 0x7e, 0xef, 0x75, 0xbf, 0xf7, 0x7e, 0xaf, + 0x77, 0x60, 0x8f, 0x46, 0x92, 0x70, 0xdc, 0x43, 0x34, 0xf2, 0x05, 0xc1, 0x03, 0x4e, 0xe5, 0xd0, + 0xc5, 0x38, 0x71, 0x63, 0xce, 0x12, 0x1a, 0x10, 0xee, 0x26, 0xbb, 0xc5, 0xb3, 0x13, 0x73, 0x26, + 0x99, 0xf9, 0xd6, 0x15, 0x36, 0x0e, 0xc6, 0x89, 0x53, 0xe8, 0x25, 0xbb, 0x37, 0xdf, 0xbe, 0x0e, + 0x38, 0xd9, 0x75, 0x9f, 0x53, 0x4e, 0x52, 0xac, 0x9b, 0xeb, 0x5d, 0xd6, 0x65, 0xfa, 0xd1, 0x55, + 0x4f, 0x99, 0x74, 0xab, 0xcb, 0x58, 0x37, 0x24, 0xae, 0x7e, 0xeb, 0x0c, 0x9e, 0xba, 0x92, 0xf6, + 0x89, 0x90, 0xa8, 0x1f, 0x67, 0x0a, 0xad, 0x71, 0x85, 0x60, 0xc0, 0x91, 0xa4, 0x2c, 0xca, 0x01, + 0x68, 0x07, 0xbb, 0x98, 0x71, 0xe2, 0xe2, 0x90, 0x92, 0x48, 0x2a, 0xaf, 0xe9, 0x53, 0xa6, 0xe0, + 0x2a, 0x85, 0x90, 0x76, 0x7b, 0x32, 0x15, 0x0b, 0x57, 0x92, 0x28, 0x20, 0xbc, 0x4f, 0x53, 0xe5, + 0xf2, 0x2d, 0x33, 0xd8, 0xac, 0xac, 0x63, 0x3e, 0x8c, 0x25, 0x73, 0x2f, 0xc8, 0x50, 0x64, 0xab, + 0xef, 0x60, 0x26, 0xfa, 0x4c, 0xb8, 0x44, 0xed, 0x3f, 0xc2, 0xc4, 0x4d, 0x76, 0x3b, 0x44, 0xa2, + 0xdd, 0x42, 0x90, 0xc7, 0x9d, 0xe9, 0x75, 0x90, 0x28, 0x75, 0x30, 0xa3, 0x79, 0xdc, 0xab, 0xa8, + 0x4f, 0x23, 0xe6, 0xea, 0xbf, 0xa9, 0xc8, 0xfe, 0xcf, 0x2c, 0x58, 0x6d, 0x16, 0x89, 0x41, 0x9f, + 0xf0, 0xfd, 0x20, 0xa0, 0x6a, 0x97, 0x27, 0x9c, 0xc5, 0x4c, 0xa0, 0xd0, 0x5c, 0x87, 0x19, 0x49, + 0x65, 0x48, 0x2c, 0x63, 0xdb, 0xd8, 0x69, 0x78, 0xe9, 0x8b, 0xb9, 0x0d, 0xcd, 0x80, 0x08, 0xcc, + 0x69, 0xac, 0x94, 0xad, 0x69, 0xbd, 0x56, 0x15, 0x99, 0x1b, 0x30, 0x9f, 0xa6, 0x86, 0x06, 0x56, + 0x4d, 0x2f, 0xcf, 0xe9, 0xf7, 0xa3, 0xc0, 0xfc, 0x10, 0x96, 0x68, 0x44, 0x25, 0x45, 0xa1, 0xdf, + 0x23, 0xea, 0x80, 0xac, 0xfa, 0xb6, 0xb1, 0xd3, 0xdc, 0xbb, 0xe9, 0xd0, 0x0e, 0x76, 0xd4, 0x99, + 0x3a, 0xd9, 0x49, 0x26, 0xbb, 0xce, 0x03, 0xad, 0x71, 0x50, 0xff, 0xe2, 0xab, 0xad, 0x29, 0x6f, + 0x31, 0xb3, 0x4b, 0x85, 0xe6, 0x2d, 0x58, 0xe8, 0x92, 0x88, 0x08, 0x2a, 0xfc, 0x1e, 0x12, 0x3d, + 0x6b, 0x66, 0xdb, 0xd8, 0x59, 0xf0, 0x9a, 0x99, 0xec, 0x01, 0x12, 0x3d, 0x73, 0x0b, 0x9a, 0x1d, + 0x1a, 0x21, 0x3e, 0x4c, 0x35, 0x66, 0xb5, 0x06, 0xa4, 0x22, 0xad, 0xd0, 0x06, 0x10, 0x31, 0x7a, + 0x1e, 0xf9, 0xaa, 0x00, 0xac, 0xb9, 0x2c, 0x90, 0x34, 0xf9, 0x4e, 0x9e, 0x7c, 0xe7, 0x2c, 0xaf, + 0x8e, 0x83, 0x79, 0x15, 0xc8, 0xa7, 0x5f, 0x6f, 0x19, 0x5e, 0x43, 0xdb, 0xa9, 0x15, 0xf3, 0x11, + 0xac, 0x0c, 0xa2, 0x0e, 0x8b, 0x02, 0x1a, 0x75, 0xfd, 0x98, 0x70, 0xca, 0x02, 0x6b, 0x5e, 0x43, + 0x6d, 0x5c, 0x82, 0x3a, 0xcc, 0xea, 0x28, 0x45, 0xfa, 0x4c, 0x21, 0x2d, 0x17, 0xc6, 0x27, 0xda, + 0xd6, 0xfc, 0x18, 0x4c, 0x8c, 0x13, 0x1d, 0x12, 0x1b, 0xc8, 0x1c, 0xb1, 0x31, 0x39, 0xe2, 0x0a, + 0xc6, 0xc9, 0x59, 0x6a, 0x9d, 0x41, 0xfe, 0x0a, 0x6e, 0x48, 0x8e, 0x22, 0xf1, 0x94, 0xf0, 0x71, + 0x5c, 0x98, 0x1c, 0xf7, 0x8d, 0x1c, 0x63, 0x14, 0xfc, 0x01, 0x6c, 0xe3, 0xac, 0x80, 0x7c, 0x4e, + 0x02, 0x2a, 0x24, 0xa7, 0x9d, 0x81, 0xb2, 0xf5, 0x9f, 0x72, 0x84, 0x75, 0x8d, 0x34, 0x75, 0x11, + 0xb4, 0x72, 0x3d, 0x6f, 0x44, 0xed, 0x83, 0x4c, 0xcb, 0x7c, 0x0c, 0xdf, 0xeb, 0x84, 0x0c, 0x5f, + 0x08, 0x15, 0x9c, 0x3f, 0x82, 0xa4, 0x5d, 0xf7, 0xa9, 0x10, 0x0a, 0x6d, 0x61, 0xdb, 0xd8, 0xa9, + 0x79, 0xb7, 0x52, 0xdd, 0x13, 0xc2, 0x0f, 0x2b, 0x9a, 0x67, 0x15, 0x45, 0xf3, 0x0e, 0x98, 0x3d, + 0x2a, 0x24, 0xe3, 0x14, 0xa3, 0xd0, 0x27, 0x91, 0xe4, 0x94, 0x08, 0x6b, 0x51, 0x9b, 0xaf, 0x96, + 0x2b, 0xf7, 0xd3, 0x05, 0xf3, 0x21, 0xdc, 0xba, 0xd6, 0xa9, 0x8f, 0x7b, 0x28, 0x8a, 0x48, 0x68, + 0x2d, 0xe9, 0xad, 0x6c, 0x05, 0xd7, 0xf8, 0x6c, 0xa7, 0x6a, 0xe6, 0x1a, 0xcc, 0x48, 0x16, 0xfb, + 0x8f, 0xac, 0xe5, 0x6d, 0x63, 0x67, 0xd1, 0xab, 0x4b, 0x16, 0x3f, 0xba, 0x37, 0xff, 0x9b, 0xcf, + 0xb7, 0xa6, 0x3e, 0xfb, 0x7c, 0x6b, 0xca, 0xfe, 0xab, 0x01, 0x37, 0xda, 0xc5, 0x69, 0xf4, 0x59, + 0x82, 0xc2, 0x6f, 0xb3, 0xeb, 0xf6, 0xa1, 0x21, 0x54, 0x38, 0xba, 0xce, 0xeb, 0xaf, 0x51, 0xe7, + 0xf3, 0xca, 0x4c, 0x2d, 0xd8, 0x7f, 0x30, 0x60, 0xfd, 0xfe, 0xb3, 0x01, 0x4d, 0x18, 0x46, 0xff, + 0x17, 0x92, 0x38, 0x86, 0x45, 0x52, 0xc1, 0x13, 0x56, 0x6d, 0xbb, 0xb6, 0xd3, 0xdc, 0x7b, 0xdb, + 0x49, 0x49, 0xcc, 0x29, 0xb8, 0x2d, 0x23, 0x32, 0xa7, 0xea, 0xdd, 0x1b, 0xb5, 0xbd, 0x37, 0x6d, + 0x19, 0xf6, 0x9f, 0x0c, 0xb8, 0xa9, 0x8e, 0xbf, 0x4b, 0x3c, 0xf2, 0x1c, 0xf1, 0xe0, 0x90, 0x44, + 0xac, 0x2f, 0xbe, 0x71, 0x9c, 0x36, 0x2c, 0x06, 0x1a, 0xc9, 0x97, 0xcc, 0x47, 0x41, 0xa0, 0xe3, + 0xd4, 0x3a, 0x4a, 0x78, 0xc6, 0xf6, 0x83, 0xc0, 0xdc, 0x81, 0x95, 0x52, 0x87, 0xab, 0x7c, 0xaa, + 0x63, 0x56, 0x6a, 0x4b, 0xb9, 0x9a, 0xce, 0x32, 0xb1, 0xff, 0x6d, 0xc0, 0xca, 0x87, 0x21, 0xeb, + 0xa0, 0xf0, 0x34, 0x44, 0xa2, 0xa7, 0x4a, 0x6f, 0xa8, 0xd2, 0xc3, 0x49, 0xd6, 0xf3, 0x3a, 0xbc, + 0x89, 0xd3, 0xa3, 0xcc, 0x34, 0x0b, 0xbd, 0x0f, 0xab, 0x45, 0x17, 0x16, 0x55, 0xa0, 0x77, 0x73, + 0xb0, 0xf6, 0xe2, 0xab, 0xad, 0xe5, 0xbc, 0xd8, 0xda, 0xba, 0x22, 0x0e, 0xbd, 0x65, 0x3c, 0x22, + 0x08, 0xcc, 0x16, 0x34, 0x69, 0x07, 0xfb, 0x82, 0x3c, 0xf3, 0xa3, 0x41, 0x5f, 0x17, 0x50, 0xdd, + 0x6b, 0xd0, 0x0e, 0x3e, 0x25, 0xcf, 0x1e, 0x0d, 0xfa, 0xe6, 0xbb, 0xf0, 0x66, 0x3e, 0x80, 0xfd, + 0x04, 0x85, 0xbe, 0xb2, 0x57, 0xc7, 0xc1, 0x75, 0x3d, 0x2d, 0x78, 0x6b, 0xf9, 0xea, 0x39, 0x0a, + 0x95, 0xb3, 0xfd, 0x20, 0xe0, 0xf6, 0xdf, 0x67, 0x60, 0xf6, 0x04, 0x71, 0xd4, 0x17, 0xe6, 0x19, + 0x2c, 0x4b, 0xd2, 0x8f, 0x43, 0x24, 0x89, 0x9f, 0x32, 0x7c, 0xb6, 0xd3, 0xdb, 0x9a, 0xf9, 0xab, + 0xc3, 0xd2, 0xa9, 0x8c, 0xc7, 0x64, 0xd7, 0x69, 0x6b, 0xe9, 0xa9, 0x44, 0x92, 0x78, 0x4b, 0x39, + 0x46, 0x2a, 0x34, 0xef, 0x82, 0x25, 0xf9, 0x40, 0xc8, 0x92, 0x7b, 0x4b, 0xd2, 0x49, 0x73, 0xf9, + 0x66, 0xbe, 0x9e, 0xd2, 0x55, 0x41, 0x36, 0x57, 0xd3, 0x6c, 0xed, 0x9b, 0xd0, 0xec, 0x29, 0xac, + 0xa9, 0x19, 0x35, 0x8e, 0x59, 0x9f, 0x1c, 0x73, 0x55, 0xd9, 0x8f, 0x82, 0x7e, 0x0c, 0x66, 0x22, + 0xf0, 0x38, 0xe6, 0xcc, 0x6b, 0xc4, 0x99, 0x08, 0x3c, 0x0a, 0x19, 0xc0, 0xa6, 0x50, 0xc5, 0xe7, + 0xf7, 0x89, 0xd4, 0xa4, 0x1d, 0x87, 0x24, 0xa2, 0xa2, 0x97, 0x83, 0xcf, 0x4e, 0x0e, 0xbe, 0xa1, + 0x81, 0x3e, 0x52, 0x38, 0x5e, 0x0e, 0x93, 0x79, 0x69, 0x43, 0xeb, 0x6a, 0x2f, 0x45, 0x82, 0xe6, + 0x74, 0x82, 0xbe, 0x73, 0x05, 0x44, 0x91, 0x25, 0x01, 0xef, 0x54, 0x86, 0x8b, 0xea, 0x6a, 0x5f, + 0x37, 0x94, 0xcf, 0x49, 0x57, 0x31, 0x30, 0x4a, 0xe7, 0x0c, 0x21, 0xc5, 0x80, 0xcc, 0xd8, 0x43, + 0x5d, 0x81, 0x0a, 0xe6, 0x68, 0x33, 0x1a, 0x65, 0xb7, 0x08, 0xbb, 0x9c, 0x41, 0x05, 0x47, 0x78, + 0x15, 0xac, 0x0f, 0x08, 0x79, 0x58, 0x9f, 0x9f, 0x5f, 0x69, 0xd8, 0x3f, 0x80, 0x86, 0x6e, 0xd1, + 0x7d, 0x7c, 0x21, 0xcc, 0x4d, 0x68, 0xa8, 0x5a, 0x27, 0x42, 0x10, 0x61, 0x19, 0xba, 0xb3, 0x4b, + 0x81, 0x2d, 0x61, 0xe3, 0xba, 0x3b, 0x94, 0x30, 0x9f, 0xc0, 0x5c, 0x4c, 0xf4, 0x80, 0xd7, 0x86, + 0xcd, 0xbd, 0xf7, 0x9c, 0x09, 0x6e, 0xb8, 0xce, 0x75, 0x80, 0x5e, 0x8e, 0x66, 0xf3, 0xf2, 0xe6, + 0x36, 0x36, 0x42, 0x84, 0x79, 0x3e, 0xee, 0xf4, 0xa7, 0xaf, 0xe5, 0x74, 0x0c, 0xaf, 0xf4, 0x79, + 0x1b, 0x9a, 0xfb, 0xe9, 0xb6, 0x7f, 0x46, 0x85, 0xbc, 0x7c, 0x2c, 0x0b, 0xd5, 0x63, 0x79, 0x08, + 0x4b, 0xd9, 0x38, 0x3c, 0x63, 0x9a, 0x66, 0xcc, 0xef, 0x02, 0x64, 0x73, 0x54, 0xd1, 0x53, 0x4a, + 0xc4, 0x8d, 0x4c, 0x72, 0x14, 0x8c, 0x4c, 0xb0, 0xe9, 0x91, 0x09, 0x66, 0x7b, 0xb0, 0x7c, 0x2e, + 0xf0, 0xcf, 0xf3, 0xbb, 0xd2, 0xe3, 0x58, 0x98, 0x6f, 0xc0, 0xac, 0xea, 0x8c, 0x0c, 0xa8, 0xee, + 0xcd, 0x24, 0x02, 0x1f, 0x69, 0x2e, 0x2e, 0xef, 0x63, 0x2c, 0xf6, 0x69, 0x20, 0xac, 0xe9, 0xed, + 0xda, 0x4e, 0xdd, 0x5b, 0x1a, 0x94, 0xe6, 0x47, 0x81, 0xb0, 0x7f, 0x01, 0xcd, 0x0a, 0xa0, 0xb9, + 0x04, 0xd3, 0x05, 0xd6, 0x34, 0x0d, 0xcc, 0x7b, 0xb0, 0x51, 0x02, 0x8d, 0x92, 0x6b, 0x8a, 0xd8, + 0xf0, 0x6e, 0x14, 0x0a, 0x23, 0xfc, 0x2a, 0xec, 0xc7, 0xb0, 0x7e, 0x54, 0xb6, 0x72, 0x41, 0xdd, + 0x23, 0x3b, 0x34, 0x46, 0x67, 0xf4, 0x26, 0x34, 0x8a, 0xdf, 0x21, 0x7a, 0xf7, 0x75, 0xaf, 0x14, + 0xd8, 0x7d, 0x58, 0x39, 0x17, 0xf8, 0x94, 0x44, 0x41, 0x09, 0x76, 0xcd, 0x01, 0x1c, 0x8c, 0x03, + 0x4d, 0x7c, 0xa9, 0x2d, 0xdd, 0x31, 0xd8, 0x38, 0x47, 0x21, 0x0d, 0x90, 0x64, 0xfc, 0x94, 0xc8, + 0x74, 0xac, 0x9e, 0x20, 0x7c, 0x41, 0xa4, 0x30, 0x3d, 0xa8, 0x87, 0x54, 0xc8, 0xac, 0xb2, 0xee, + 0x5e, 0x5b, 0x59, 0xc9, 0xae, 0x73, 0x1d, 0xc8, 0x21, 0x92, 0x28, 0xeb, 0x48, 0x8d, 0x65, 0x7f, + 0x1f, 0xd6, 0x3e, 0x42, 0x72, 0xc0, 0x49, 0x30, 0x92, 0xe3, 0x15, 0xa8, 0xa9, 0xfc, 0x19, 0x3a, + 0x7f, 0xea, 0x51, 0x4d, 0x79, 0xeb, 0xfe, 0x27, 0x31, 0xe3, 0x92, 0x04, 0x97, 0x4e, 0xe4, 0x15, + 0xc7, 0x7b, 0x01, 0x6b, 0xea, 0xb0, 0x04, 0x89, 0x02, 0xbf, 0xd8, 0x67, 0x9a, 0xc7, 0xe6, 0xde, + 0x4f, 0x26, 0xea, 0x8e, 0x71, 0x77, 0xd9, 0x06, 0x56, 0x93, 0x31, 0xb9, 0xb0, 0x7f, 0x67, 0x80, + 0x75, 0x4c, 0x86, 0xfb, 0x42, 0xd0, 0x6e, 0xd4, 0x27, 0x91, 0x54, 0xcc, 0x86, 0x30, 0x51, 0x8f, + 0xe6, 0x5b, 0xb0, 0x58, 0x4c, 0x52, 0x3d, 0x40, 0x0d, 0x3d, 0x40, 0x17, 0x72, 0xa1, 0x6a, 0x30, + 0xf3, 0x1e, 0x40, 0xcc, 0x49, 0xe2, 0x63, 0xff, 0x82, 0x0c, 0xb3, 0x2c, 0x6e, 0x56, 0x07, 0x63, + 0xfa, 0x2b, 0xd1, 0x39, 0x19, 0x74, 0x42, 0x8a, 0x8f, 0xc9, 0xd0, 0x9b, 0x57, 0xfa, 0xed, 0x63, + 0x32, 0x54, 0x37, 0x9d, 0x98, 0x3d, 0x27, 0x5c, 0x4f, 0xb3, 0x9a, 0x97, 0xbe, 0xd8, 0xbf, 0x37, + 0xe0, 0x46, 0x91, 0x8e, 0xbc, 0x5c, 0x4f, 0x06, 0x1d, 0x65, 0xf1, 0x8a, 0x73, 0xbb, 0x14, 0xed, + 0xf4, 0x15, 0xd1, 0xbe, 0x0f, 0x0b, 0x45, 0x83, 0xa8, 0x78, 0x6b, 0x13, 0xc4, 0xdb, 0xcc, 0x2d, + 0x8e, 0xc9, 0xd0, 0xfe, 0x75, 0x25, 0xb6, 0x83, 0x61, 0x85, 0xfb, 0xf8, 0xff, 0x88, 0xad, 0x70, + 0x5b, 0x8d, 0x0d, 0x57, 0xed, 0x2f, 0x6d, 0xa0, 0x76, 0x79, 0x03, 0xf6, 0x1f, 0x0d, 0x58, 0xaf, + 0x7a, 0x15, 0x67, 0xec, 0x84, 0x0f, 0x22, 0xf2, 0x2a, 0xef, 0x65, 0xfb, 0x4d, 0x57, 0xdb, 0xef, + 0x09, 0x2c, 0x8d, 0x04, 0x25, 0xb2, 0xd3, 0xf8, 0xd1, 0x44, 0x35, 0x56, 0x61, 0x57, 0x6f, 0xb1, + 0xba, 0x0f, 0x61, 0xff, 0xd6, 0x28, 0xc7, 0x4c, 0x3a, 0xbd, 0xc4, 0x7e, 0x18, 0x66, 0x57, 0x60, + 0x33, 0x86, 0xb9, 0x74, 0x40, 0x8a, 0xac, 0x2f, 0x37, 0xaf, 0x1c, 0x85, 0x87, 0x04, 0xeb, 0x69, + 0x78, 0x57, 0x95, 0xee, 0x5f, 0xbe, 0xde, 0xba, 0xdd, 0xa5, 0xb2, 0x37, 0xe8, 0x38, 0x98, 0xf5, + 0xdd, 0xec, 0xeb, 0x41, 0xfa, 0xef, 0x8e, 0x08, 0x2e, 0x5c, 0x39, 0x8c, 0x89, 0xc8, 0x6d, 0xc4, + 0x9f, 0xff, 0xf5, 0xb7, 0x1f, 0x1a, 0x5e, 0xee, 0xe6, 0xe0, 0xc9, 0x17, 0x2f, 0x5a, 0xc6, 0x97, + 0x2f, 0x5a, 0xc6, 0x3f, 0x5f, 0xb4, 0x8c, 0x4f, 0x5f, 0xb6, 0xa6, 0xbe, 0x7c, 0xd9, 0x9a, 0xfa, + 0xc7, 0xcb, 0xd6, 0xd4, 0x2f, 0xdf, 0xbb, 0x0c, 0x5a, 0xee, 0xfd, 0x4e, 0xf1, 0xbd, 0x26, 0xf9, + 0xb1, 0xfb, 0xc9, 0xe8, 0xd7, 0x20, 0xed, 0xaf, 0x33, 0xab, 0x59, 0xea, 0xdd, 0xff, 0x06, 0x00, + 0x00, 0xff, 0xff, 0x2a, 0x2c, 0xd4, 0x2e, 0x3e, 0x12, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -2596,6 +2649,43 @@ func (m *ConsumerAddrsToPrune) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ConsumerRewardsAllocation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConsumerRewardsAllocation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsumerRewardsAllocation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Rewards) > 0 { + for iNdEx := len(m.Rewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Rewards[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProvider(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintProvider(dAtA []byte, offset int, v uint64) int { offset -= sovProvider(v) base := offset @@ -3067,6 +3157,21 @@ func (m *ConsumerAddrsToPrune) Size() (n int) { return n } +func (m *ConsumerRewardsAllocation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Rewards) > 0 { + for _, e := range m.Rewards { + l = e.Size() + n += 1 + l + sovProvider(uint64(l)) + } + } + return n +} + func sovProvider(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -6360,6 +6465,90 @@ func (m *ConsumerAddrsToPrune) Unmarshal(dAtA []byte) error { } return nil } +func (m *ConsumerRewardsAllocation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConsumerRewardsAllocation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsumerRewardsAllocation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rewards", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Rewards = append(m.Rewards, types2.DecCoin{}) + if err := m.Rewards[len(m.Rewards)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProvider(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProvider + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipProvider(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index df531e09ca..761eca90d7 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -11,6 +11,7 @@ import ( ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" "cosmossdk.io/math" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" sdk "github.com/cosmos/cosmos-sdk/types" auth "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -109,6 +110,10 @@ type ClientKeeper interface { // DistributionKeeper defines the expected interface of the distribution keeper type DistributionKeeper interface { FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error + GetFeePool(ctx sdk.Context) distrtypes.FeePool + SetFeePool(ctx sdk.Context, feePool distrtypes.FeePool) + GetCommunityTax(ctx sdk.Context) math.LegacyDec + AllocateTokensToValidator(ctx sdk.Context, validator stakingtypes.ValidatorI, reward sdk.DecCoins) } // ConsumerHooks event hooks for newly bonded cross-chain validators