diff --git a/.changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md b/.changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md new file mode 100644 index 0000000000..b4ff9d6341 --- /dev/null +++ b/.changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md @@ -0,0 +1,3 @@ +- Only start distributing rewards to validators after they have been validating + for a fixed number of blocks. Introduces the `NumberOfEpochsToStartReceivingRewards` param. + ([\#1929](https://github.com/cosmos/interchain-security/pull/1929)) diff --git a/.changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md b/.changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md new file mode 100644 index 0000000000..b4ff9d6341 --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md @@ -0,0 +1,3 @@ +- Only start distributing rewards to validators after they have been validating + for a fixed number of blocks. Introduces the `NumberOfEpochsToStartReceivingRewards` param. + ([\#1929](https://github.com/cosmos/interchain-security/pull/1929)) diff --git a/docs/docs/validators/withdraw_rewards.md b/docs/docs/validators/withdraw_rewards.md index 1a9ab1fb3c..15d27b3006 100644 --- a/docs/docs/validators/withdraw_rewards.md +++ b/docs/docs/validators/withdraw_rewards.md @@ -2,8 +2,17 @@ sidebar_position: 3 --- -# Withdrawing consumer chain validator rewards +# Consumer chain validator rewards +:::warning +A validator can only receive rewards from a consumer chain if the validator has been validating the consumer chain +for some time. Specifically, the validator has to be a consumer validator of the consumer chain for at least +`NumberOfEpochsToStartReceivingRewards * BlocksPerEpoch` blocks (run `interchain-security-pd query provider params` for +the actual values of the `NumberOfEpochsToStartReceivingRewards` and `BlocksPerEpoch` params). +::: + + +## Withdrawing rewards Here are example steps for withdrawing rewards from consumer chains in the provider chain :::info diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 339d94a833..26648c3fc5 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -251,6 +251,9 @@ message Params { // The number of blocks that comprise an epoch. int64 blocks_per_epoch = 10; + + // The number of epochs a validator has to validate a consumer chain in order to start receiving rewards from that chain. + int64 number_of_epochs_to_start_receiving_rewards = 11; } // SlashAcks contains cons addresses of consumer chain validators @@ -368,6 +371,11 @@ message ConsumerValidator { int64 power = 2; // public key the validator uses on the consumer chain during this epoch tendermint.crypto.PublicKey consumer_public_key = 3; + // height the validator had when it FIRST became a consumer validator + // If a validator becomes a consumer validator at height `H` and is continuously a consumer validator for all the upcoming + // epochs, then the height of the validator SHOULD remain `H`. This height only resets to a different height if a validator + // stops being a consumer validator during an epoch and later becomes again a consumer validator. + int64 join_height = 4; } // ConsumerRewardsAllocation stores the rewards allocated by a consumer chain // to the consumer rewards pool. It is used to allocate the tokens to the consumer diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index acff226828..2e5f2f0bbb 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -137,6 +137,14 @@ func (s *CCVTestSuite) TestRewardsDistribution() { } consuValsRewards := consumerValsOutstandingRewardsFunc(s.providerCtx()) + // increase the block height so validators are eligible for consumer rewards (see `IsEligibleForConsumerRewards`) + numberOfBlocksToStartReceivingRewards := + providerKeeper.GetNumberOfEpochsToStartReceivingRewards(s.providerCtx()) * providerKeeper.GetBlocksPerEpoch(s.providerCtx()) + + for s.providerCtx().BlockHeight() <= numberOfBlocksToStartReceivingRewards { + s.providerChain.NextBlock() + } + // Transfer rewards from consumer to provider and distribute rewards to // validators and community pool by calling BeginBlockRD relayAllCommittedPackets( @@ -711,9 +719,14 @@ func (s *CCVTestSuite) TestAllocateTokens() { totalRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100))} + // increase the block height so validators are eligible for consumer rewards (see `IsEligibleForConsumerRewards`) + numberOfBlocksToStartReceivingRewards := providerKeeper.GetNumberOfEpochsToStartReceivingRewards( + s.providerCtx()) * providerKeeper.GetBlocksPerEpoch(s.providerCtx()) + providerCtx := s.providerCtx().WithBlockHeight(numberOfBlocksToStartReceivingRewards + s.providerCtx().BlockHeight()) + // fund consumer rewards pool bankKeeper.SendCoinsFromAccountToModule( - s.providerCtx(), + providerCtx, s.providerChain.SenderAccount.GetAddress(), providertypes.ConsumerRewardsPool, totalRewards, @@ -724,7 +737,7 @@ func (s *CCVTestSuite) TestAllocateTokens() { for chainID := range s.consumerBundles { // update consumer allocation providerKeeper.SetConsumerRewardsAllocation( - s.providerCtx(), + providerCtx, chainID, providertypes.ConsumerRewardsAllocation{ Rewards: sdk.NewDecCoinsFromCoins(rewardsPerChain...), @@ -735,7 +748,7 @@ func (s *CCVTestSuite) TestAllocateTokens() { // iterate over the validators and verify that no validator has outstanding rewards totalValsRewards := sdk.DecCoins{} for _, val := range s.providerChain.Vals.Validators { - valRewards, err := distributionKeeper.GetValidatorOutstandingRewards(s.providerCtx(), sdk.ValAddress(val.Address)) + valRewards, err := distributionKeeper.GetValidatorOutstandingRewards(providerCtx, sdk.ValAddress(val.Address)) s.Require().NoError(err) totalValsRewards = totalValsRewards.Add(valRewards.Rewards...) } @@ -745,17 +758,17 @@ func (s *CCVTestSuite) TestAllocateTokens() { // At this point the distribution module account // only holds the community pool's tokens // since there are no validators with outstanding rewards - lastCommPool := getDistrAcctBalFn(s.providerCtx()) + lastCommPool := getDistrAcctBalFn(providerCtx) // execute BeginBlock to trigger the token allocation - providerKeeper.BeginBlockRD(s.providerCtx()) + providerKeeper.BeginBlockRD(providerCtx) valNum := len(s.providerChain.Vals.Validators) consNum := len(s.consumerBundles) // compute the expected validators token allocation by subtracting the community tax rewardsPerChainDec := sdk.NewDecCoinsFromCoins(rewardsPerChain...) - communityTax, err := distributionKeeper.GetCommunityTax(s.providerCtx()) + communityTax, err := distributionKeeper.GetCommunityTax(providerCtx) s.Require().NoError(err) rewardsPerChainTrunc, _ := rewardsPerChainDec. @@ -767,7 +780,7 @@ func (s *CCVTestSuite) TestAllocateTokens() { // verify the validator tokens allocation // note that the validators have the same voting power to keep things simple for _, val := range s.providerChain.Vals.Validators { - valRewards, err := distributionKeeper.GetValidatorOutstandingRewards(s.providerCtx(), sdk.ValAddress(val.Address)) + valRewards, err := distributionKeeper.GetValidatorOutstandingRewards(providerCtx, sdk.ValAddress(val.Address)) s.Require().NoError(err) s.Require().Equal( @@ -779,13 +792,13 @@ func (s *CCVTestSuite) TestAllocateTokens() { // check that the total expected rewards are transferred to the distribution module account // store the decimal remainders in the consumer reward allocations - allocRemainderPerChain := providerKeeper.GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID).Rewards + allocRemainderPerChain := providerKeeper.GetConsumerRewardsAllocation(providerCtx, s.consumerChain.ChainID).Rewards // compute the total rewards distributed to the distribution module balance (validator outstanding rewards + community pool tax), totalRewardsDistributed := sdk.NewDecCoinsFromCoins(totalRewards...).Sub(allocRemainderPerChain.MulDec(math.LegacyNewDec(int64(consNum)))) // compare the expected total rewards against the distribution module balance - s.Require().Equal(lastCommPool.Add(totalRewardsDistributed...), getDistrAcctBalFn(s.providerCtx())) + s.Require().Equal(lastCommPool.Add(totalRewardsDistributed...), getDistrAcctBalFn(providerCtx)) } // getEscrowBalance gets the current balances in the escrow account holding the transferred tokens to the provider @@ -820,7 +833,7 @@ func (s *CCVTestSuite) prepareRewardDist() { s.coordinator.CommitNBlocks(s.consumerChain, uint64(blocksToGo)) } -func (s *CCVTestSuite) TestAllocateTokensToValidator() { +func (s *CCVTestSuite) TestAllocateTokensToConsumerValidators() { providerKeeper := s.providerApp.GetProviderKeeper() distributionKeeper := s.providerApp.GetTestDistributionKeeper() bankKeeper := s.providerApp.GetTestBankKeeper() @@ -866,6 +879,10 @@ func (s *CCVTestSuite) TestAllocateTokensToValidator() { s.Run(tc.name, func() { ctx, _ := s.providerCtx().CacheContext() + // increase the block height so validators are eligible for consumer rewards (see `IsEligibleForConsumerRewards`) + ctx = ctx.WithBlockHeight(providerKeeper.GetNumberOfEpochsToStartReceivingRewards(ctx)*providerKeeper.GetBlocksPerEpoch(ctx) + + ctx.BlockHeight()) + // change the consumer valset consuVals := providerKeeper.GetConsumerValSet(ctx, chainID) providerKeeper.DeleteConsumerValSet(ctx, chainID) @@ -948,6 +965,116 @@ func (s *CCVTestSuite) TestAllocateTokensToValidator() { } } +// TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights tests `AllocateTokensToConsumerValidators` with +// consumer validators that have different heights. Specifically, test that validators that have been consumer validators +// for some time receive rewards, while validators that recently became consumer validators do not receive rewards. +func (s *CCVTestSuite) TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights() { + // Note this test is an adaptation of a `TestAllocateTokensToConsumerValidators` testcase. + providerKeeper := s.providerApp.GetProviderKeeper() + distributionKeeper := s.providerApp.GetTestDistributionKeeper() + bankKeeper := s.providerApp.GetTestBankKeeper() + + chainID := s.consumerChain.ChainID + + tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDecFromIntWithPrec(math.NewInt(999), 2))} + rate := math.LegacyOneDec() + expAllocated := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDecFromIntWithPrec(math.NewInt(999), 2))} + + ctx, _ := s.providerCtx().CacheContext() + // If the provider chain has not yet reached `GetNumberOfEpochsToStartReceivingRewards * GetBlocksPerEpoch` block height, + // then all validators receive rewards (see `IsEligibleForConsumerRewards`). In this test, we want to check whether + // validators receive rewards or not based on how long they have been consumer validators. Because of this, we increase the block height. + ctx = ctx.WithBlockHeight(providerKeeper.GetNumberOfEpochsToStartReceivingRewards(ctx)*providerKeeper.GetBlocksPerEpoch(ctx) + 1) + + // update the consumer validators + consuVals := providerKeeper.GetConsumerValSet(ctx, chainID) + // first 2 validators were consumer validators since block height 1 and hence get rewards + consuVals[0].JoinHeight = 1 + consuVals[1].JoinHeight = 1 + // last 2 validators were consumer validators since block height 2 and hence do not get rewards because they + // have not been consumer validators for `GetNumberOfEpochsToStartReceivingRewards * GetBlocksPerEpoch` blocks + consuVals[2].JoinHeight = 2 + consuVals[3].JoinHeight = 2 + providerKeeper.SetConsumerValSet(ctx, chainID, consuVals) + + providerKeeper.DeleteConsumerValSet(ctx, chainID) + providerKeeper.SetConsumerValSet(ctx, chainID, consuVals) + consuVals = providerKeeper.GetConsumerValSet(ctx, chainID) + + // set the same consumer commission rate for all consumer validators + for _, v := range consuVals { + provAddr := providertypes.NewProviderConsAddress(sdk.ConsAddress(v.ProviderConsAddr)) + err := providerKeeper.SetConsumerCommissionRate( + ctx, + chainID, + provAddr, + rate, + ) + s.Require().NoError(err) + } + + // allocate tokens + res := providerKeeper.AllocateTokensToConsumerValidators( + ctx, + chainID, + tokens, + ) + + // check that the expected result is returned + s.Require().Equal(expAllocated, res) + + // rewards are expected to be allocated evenly between validators 3 and 4 + rewardsPerVal := expAllocated.QuoDec(math.LegacyNewDec(int64(2))) + + // assert that the rewards are allocated to the first 2 validators + for _, v := range consuVals[0:2] { + valAddr := sdk.ValAddress(v.ProviderConsAddr) + rewards, err := s.providerApp.GetTestDistributionKeeper().GetValidatorOutstandingRewards( + ctx, + valAddr, + ) + s.Require().NoError(err) + s.Require().Equal(rewardsPerVal, rewards.Rewards) + + // send rewards to the distribution module + valRewardsTrunc, _ := rewards.Rewards.TruncateDecimal() + err = bankKeeper.SendCoinsFromAccountToModule( + ctx, + s.providerChain.SenderAccount.GetAddress(), + distrtypes.ModuleName, + valRewardsTrunc) + s.Require().NoError(err) + + // check that validators can withdraw their rewards + withdrawnCoins, err := distributionKeeper.WithdrawValidatorCommission( + ctx, + valAddr, + ) + s.Require().NoError(err) + + // check that the withdrawn coins is equal to the entire reward amount + // times the set consumer commission rate + commission := rewards.Rewards.MulDec(rate) + c, _ := commission.TruncateDecimal() + s.Require().Equal(withdrawnCoins, c) + + // check that validators get rewards in their balance + s.Require().Equal(withdrawnCoins, bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddr))) + } + + // assert that no rewards are allocated to the last 2 validators because they have not been consumer validators + // for at least `GetNumberOfEpochsToStartReceivingRewards * GetBlocksPerEpoch` blocks + for _, v := range consuVals[2:4] { + valAddr := sdk.ValAddress(v.ProviderConsAddr) + rewards, err := s.providerApp.GetTestDistributionKeeper().GetValidatorOutstandingRewards( + ctx, + valAddr, + ) + s.Require().NoError(err) + s.Require().Zero(rewards.Rewards) + } +} + // TestMultiConsumerRewardsDistribution tests the rewards distribution of multiple consumers chains func (s *CCVTestSuite) TestMultiConsumerRewardsDistribution() { s.SetupAllCCVChannels() diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index 9ac016abbc..64d7f3fcc1 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -85,6 +85,10 @@ func (k Keeper) AllocateTokens(ctx sdk.Context) { continue } + // if rewardsCollected.IsZero() { + // continue + // } + // temporary workaround to keep CanWithdrawInvariant happy // general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634 if k.ComputeConsumerTotalVotingPower(ctx, consumerChainID) == 0 { @@ -163,6 +167,15 @@ func (k Keeper) AllocateTokens(ctx sdk.Context) { } } +// IsEligibleForConsumerRewards returns `true` if the validator with `consumerValidatorHeight` has been a consumer +// validator for a long period of time and hence is eligible to receive rewards, and false otherwise +func (k Keeper) IsEligibleForConsumerRewards(ctx sdk.Context, consumerValidatorHeight int64) bool { + numberOfBlocksToStartReceivingRewards := k.GetNumberOfEpochsToStartReceivingRewards(ctx) * k.GetBlocksPerEpoch(ctx) + + // a validator is eligible for rewards if it has been a consumer validator for `NumberOfEpochsToStartReceivingRewards` epochs + return (ctx.BlockHeight() - consumerValidatorHeight) >= numberOfBlocksToStartReceivingRewards +} + // AllocateTokensToConsumerValidators allocates tokens // to the given consumer chain's validator set func (k Keeper) AllocateTokensToConsumerValidators( @@ -183,6 +196,11 @@ func (k Keeper) AllocateTokensToConsumerValidators( // Allocate tokens by iterating over the consumer validators for _, consumerVal := range k.GetConsumerValSet(ctx, chainID) { + // if a validator is not eligible, this means that the other eligible validators would get more rewards + if !k.IsEligibleForConsumerRewards(ctx, consumerVal.JoinHeight) { + continue + } + consAddr := sdk.ConsAddress(consumerVal.ProviderConsAddr) // get the validator tokens fraction using its voting power @@ -253,6 +271,12 @@ func (k Keeper) GetConsumerRewardsPool(ctx sdk.Context) sdk.Coins { func (k Keeper) ComputeConsumerTotalVotingPower(ctx sdk.Context, chainID string) (totalPower int64) { // sum the consumer validators set voting powers for _, v := range k.GetConsumerValSet(ctx, chainID) { + + // only consider the voting power of a validator that would receive rewards (i.e., validator has been validating for a number of blocks) + if !k.IsEligibleForConsumerRewards(ctx, v.JoinHeight) { + continue + } + totalPower += v.Power } diff --git a/x/ccv/provider/keeper/distribution_test.go b/x/ccv/provider/keeper/distribution_test.go index 0b22260a2b..ba79458e00 100644 --- a/x/ccv/provider/keeper/distribution_test.go +++ b/x/ccv/provider/keeper/distribution_test.go @@ -24,6 +24,14 @@ func TestComputeConsumerTotalVotingPower(t *testing.T) { keeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + // `ComputeConsumerTotalVotingPower` used in this test retrieves the blocks per epoch, so we need to set this param + params := providertypes.DefaultParams() + params.BlocksPerEpoch = 1 + keeper.SetParams(ctx, params) + + // increase the block height so validators are eligible for consumer rewards (see `IsEligibleForConsumerRewards`) + ctx = ctx.WithBlockHeight(params.NumberOfEpochsToStartReceivingRewards * params.BlocksPerEpoch) + createVal := func(power int64) tmtypes.Validator { signer := tmtypes.NewMockPV() val := tmtypes.NewValidator(signer.PrivKey.PubKey(), power) @@ -271,3 +279,21 @@ func TestGetConsumerRewardsAllocationNil(t *testing.T) { } require.Equal(t, expectedRewardAllocation, alloc) } + +func TestIsEligibleForConsumerRewards(t *testing.T) { + keeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + params := providertypes.DefaultParams() + params.NumberOfEpochsToStartReceivingRewards = 10 + params.BlocksPerEpoch = 5 + keeper.SetParams(ctx, params) + + numberOfBlocks := params.NumberOfEpochsToStartReceivingRewards * params.BlocksPerEpoch + + require.False(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks-1), 0)) + require.True(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks), 0)) + require.True(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks+1), 0)) + require.True(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks+1), 1)) + require.False(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks+1), 2)) +} diff --git a/x/ccv/provider/keeper/params.go b/x/ccv/provider/keeper/params.go index 9c1d276d4f..da3dda92b7 100644 --- a/x/ccv/provider/keeper/params.go +++ b/x/ccv/provider/keeper/params.go @@ -78,6 +78,13 @@ func (k Keeper) GetBlocksPerEpoch(ctx sdk.Context) int64 { return params.BlocksPerEpoch } +// GetNumberOfEpochsToStartReceivingRewards returns the number of epochs needed by a validator to continuously validate +// to start receiving rewards +func (k Keeper) GetNumberOfEpochsToStartReceivingRewards(ctx sdk.Context) int64 { + params := k.GetParams(ctx) + return params.NumberOfEpochsToStartReceivingRewards +} + // GetParams returns the paramset for the provider module func (k Keeper) GetParams(ctx sdk.Context) types.Params { store := ctx.KVStore(k.storeKey) diff --git a/x/ccv/provider/keeper/params_test.go b/x/ccv/provider/keeper/params_test.go index 6450102c2a..cb1dc3cfe0 100644 --- a/x/ccv/provider/keeper/params_test.go +++ b/x/ccv/provider/keeper/params_test.go @@ -50,6 +50,7 @@ func TestParams(t *testing.T) { Amount: math.NewInt(10000000), }, 600, + 24, ) providerKeeper.SetParams(ctx, newParams) params = providerKeeper.GetParams(ctx) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 650179c274..3cd957f61a 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -654,7 +654,8 @@ func TestMakeConsumerGenesis(t *testing.T) { Denom: "stake", Amount: math.NewInt(1000000), }, - BlocksPerEpoch: 600, + BlocksPerEpoch: 600, + NumberOfEpochsToStartReceivingRewards: 24, } providerKeeper.SetParams(ctx, moduleParams) defer ctrl.Finish() diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 59626f7038..b6487e8ec1 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -23,6 +23,7 @@ import ( cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) @@ -85,6 +86,55 @@ func TestQueueVSCPackets(t *testing.T) { } } +// TestQueueVSCPacketsDoesNotResetConsumerValidatorsHeights checks that the heights of consumer validators are not +// getting incorrectly updated +func TestQueueVSCPacketsDoesNotResetConsumerValidatorsHeights(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainHeight := int64(987654321) + ctx = ctx.WithBlockHeight(chainHeight) + providerKeeper.SetParams(ctx, providertypes.DefaultParams()) + + // mock 2 bonded validators + valA := createStakingValidator(ctx, mocks, 1, 1, 1) + valAConsAddr, _ := valA.GetConsAddr() + valAPubKey, _ := valA.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valAConsAddr).Return(valA, nil).AnyTimes() + valB := createStakingValidator(ctx, mocks, 2, 2, 2) + valBConsAddr, _ := valB.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(valB, nil).AnyTimes() + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 2, []stakingtypes.Validator{valA, valB}, []int64{1, 2}, -1) + + // set a consumer client, so we have a consumer chain (i.e., `k.GetAllConsumerChains(ctx)` is non empty) + providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + + // opt in validator A and set as a consumer validator + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valAConsAddr)) + consumerValidatorA := types.ConsumerValidator{ + ProviderConsAddr: valAConsAddr, + Power: 1, + ConsumerPublicKey: &valAPubKey, + JoinHeight: 123456789, + } + providerKeeper.SetConsumerValidator(ctx, "chainID", consumerValidatorA) + + // Opt in validator B. Note that validator B is not a consumer validator and hence would become a consumer + // validator for the first time after the `QueueVSCPackets` call. + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valBConsAddr)) + + providerKeeper.QueueVSCPackets(ctx) + + // the height of consumer validator A should not be modified because A was already a consumer validator + cv, _ := providerKeeper.GetConsumerValidator(ctx, "chainID", providertypes.NewProviderConsAddress(valAConsAddr)) + require.Equal(t, consumerValidatorA.JoinHeight, cv.JoinHeight, "the consumer validator's height was erroneously modified") + + // the height of consumer validator B is set to be the same as the one of the current chain height because this + // consumer validator becomes a consumer validator for the first time (i.e., was not a consumer validator in the previous epoch) + cv, _ = providerKeeper.GetConsumerValidator(ctx, "chainID", providertypes.NewProviderConsAddress(valBConsAddr)) + require.Equal(t, chainHeight, cv.JoinHeight, "the consumer validator's height was not correctly set") +} + // TestOnRecvVSCMaturedPacket tests the OnRecvVSCMaturedPacket method of the keeper. // // Note: Handling logic itself is not tested here. diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index 4f8a127884..d7f764171e 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -73,6 +73,23 @@ func (k Keeper) IsConsumerValidator(ctx sdk.Context, chainID string, providerAdd return store.Get(types.ConsumerValidatorKey(chainID, providerAddr.ToSdkConsAddr())) != nil } +// GetConsumerValidator returns the consumer validator with `providerAddr` if it exists for chain `chainID` +func (k Keeper) GetConsumerValidator(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) (types.ConsumerValidator, bool) { + store := ctx.KVStore(k.storeKey) + marshalledConsumerValidator := store.Get(types.ConsumerValidatorKey(chainID, providerAddr.ToSdkConsAddr())) + + if marshalledConsumerValidator == nil { + return types.ConsumerValidator{}, false + } + + var validator types.ConsumerValidator + if err := validator.Unmarshal(marshalledConsumerValidator); err != nil { + panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err)) + } + + return validator, true +} + // GetConsumerValSet returns all the consumer validators for chain `chainID` func (k Keeper) GetConsumerValSet( ctx sdk.Context, @@ -157,10 +174,18 @@ func (k Keeper) CreateConsumerValidator(ctx sdk.Context, chainID string, validat } } + height := ctx.BlockHeight() + if v, found := k.GetConsumerValidator(ctx, chainID, types.ProviderConsAddress{Address: consAddr}); found { + // if validator was already a consumer validator, then do not update the height set the first time + // the validator became a consumer validator + height = v.JoinHeight + } + return types.ConsumerValidator{ ProviderConsAddr: consAddr, Power: power, ConsumerPublicKey: &consumerPublicKey, + JoinHeight: height, }, nil } diff --git a/x/ccv/provider/migrations/migrator.go b/x/ccv/provider/migrations/migrator.go index b0812549d1..b031f9705b 100644 --- a/x/ccv/provider/migrations/migrator.go +++ b/x/ccv/provider/migrations/migrator.go @@ -11,6 +11,7 @@ import ( v4 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v4" v5 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v5" v6 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v6" + v7 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v7" ) // Migrator is a struct for handling in-place store migrations. @@ -53,11 +54,16 @@ func (m Migrator) Migrate4to5(ctx sdktypes.Context) error { return nil } -// Migrate5to6 migrates x/ccvprovider state from consensus version 5 to 6. -// The migration consists of -// - computing and storing the minimal power in the top N for all registered consumer chains. -// - initializing new provider chain params using params from the legacy store. +// Migrate5to6 consists of setting the `NumberOfEpochsToStartReceivingRewards` param, as well as +// computing and storing the minimal power in the top N for all registered consumer chains. func (m Migrator) Migrate5to6(ctx sdktypes.Context) error { + v6.MigrateParams(ctx, m.paramSpace) v6.MigrateMinPowerInTopN(ctx, m.providerKeeper) - return v6.MigrateLegacyParams(ctx, m.providerKeeper, m.paramSpace) + return nil +} + +// Migrate6to7 migrates x/ccvprovider state from consensus version 6 to 7. +// The migration consists of initializing new provider chain params using params from the legacy store. +func (m Migrator) Migrate6to7(ctx sdktypes.Context) error { + return v7.MigrateLegacyParams(ctx, m.providerKeeper, m.paramSpace) } diff --git a/x/ccv/provider/migrations/v6/migration_test.go b/x/ccv/provider/migrations/v6/migration_test.go index f66c73301f..9396a3e05d 100644 --- a/x/ccv/provider/migrations/v6/migration_test.go +++ b/x/ccv/provider/migrations/v6/migration_test.go @@ -7,51 +7,21 @@ import ( testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func TestMigrateParams(t *testing.T) { - t.Helper() inMemParams := testutil.NewInMemKeeperParams(t) - k, ctx, ctrl, _ := testutil.GetProviderKeeperAndCtx(t, inMemParams) + _, ctx, ctrl, _ := testutil.GetProviderKeeperAndCtx(t, inMemParams) defer ctrl.Finish() - if !inMemParams.ParamsSubspace.HasKeyTable() { - inMemParams.ParamsSubspace.WithKeyTable(providertypes.ParamKeyTable()) - } + // initially number of epochs param does not exist + require.False(t, inMemParams.ParamsSubspace.Has(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards)) - defaultParams := providertypes.DefaultParams() - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyTemplateClient, defaultParams.TemplateClient) - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyTrustingPeriodFraction, defaultParams.TrustingPeriodFraction) - inMemParams.ParamsSubspace.Set(ctx, ccvtypes.KeyCCVTimeoutPeriod, defaultParams.CcvTimeoutPeriod) - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyInitTimeoutPeriod, defaultParams.InitTimeoutPeriod) - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyVscTimeoutPeriod, defaultParams.VscTimeoutPeriod) - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeySlashMeterReplenishPeriod, defaultParams.SlashMeterReplenishPeriod) - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeySlashMeterReplenishFraction, defaultParams.SlashMeterReplenishFraction) - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyConsumerRewardDenomRegistrationFee, defaultParams.ConsumerRewardDenomRegistrationFee) - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyBlocksPerEpoch, defaultParams.BlocksPerEpoch) + MigrateParams(ctx, *inMemParams.ParamsSubspace) - // confirms that inMemParams.ParamsSubspace works as expected - require.NotPanics(t, func() { - GetParamsLegacy(ctx, inMemParams.ParamsSubspace) - }) - - // no "new" params should be available before migration - // "new" params are stored under providertypes.ParametersKey() - emptyParams := k.GetParams(ctx) - require.Empty(t, emptyParams) - - // make sure that the legacy params are equal to the default params (they were set using inMemParams.ParamsSubspace.Set()) - legacyParams := GetParamsLegacy(ctx, inMemParams.ParamsSubspace) - require.NotNil(t, legacyParams) - require.Equal(t, defaultParams, legacyParams) - - err := MigrateLegacyParams(ctx, k, inMemParams.ParamsSubspace) - require.NoError(t, err) - - // check that "new" params are available after migration and equal to defaults - migratedParams := k.GetParams(ctx) - require.NotEmpty(t, migratedParams) - require.Equal(t, defaultParams, migratedParams) - require.NotEqual(t, emptyParams, migratedParams) + // after migration, number of epochs to start receiving rewards param should exist and be equal to default + require.True(t, inMemParams.ParamsSubspace.Has(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards)) + var numberOfEpochsParam int64 + inMemParams.ParamsSubspace.Get(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards, &numberOfEpochsParam) + require.Equal(t, providertypes.DefaultNumberOfEpochsToStartReceivingRewards, numberOfEpochsParam) } diff --git a/x/ccv/provider/migrations/v6/migrations.go b/x/ccv/provider/migrations/v6/migrations.go index 87977870c4..5829f29222 100644 --- a/x/ccv/provider/migrations/v6/migrations.go +++ b/x/ccv/provider/migrations/v6/migrations.go @@ -2,22 +2,18 @@ package v6 import ( sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) -// MigrateParams migrates the provider module's parameters from the x/params to self store. -func MigrateLegacyParams(ctx sdk.Context, keeper providerkeeper.Keeper, legacyParamspace ccvtypes.LegacyParamSubspace) error { - ctx.Logger().Info("starting provider legacy params migration") - params := GetParamsLegacy(ctx, legacyParamspace) - err := params.Validate() - if err != nil { - return err +// MigrateParams adds missing provider chain params to the param store. +func MigrateParams(ctx sdk.Context, paramsSubspace paramtypes.Subspace) { + if !paramsSubspace.HasKeyTable() { + paramsSubspace.WithKeyTable(providertypes.ParamKeyTable()) } - - keeper.SetParams(ctx, params) - keeper.Logger(ctx).Info("successfully migrated legacy provider parameters") - return nil + paramsSubspace.Set(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards, providertypes.DefaultNumberOfEpochsToStartReceivingRewards) } func MigrateMinPowerInTopN(ctx sdk.Context, providerKeeper providerkeeper.Keeper) { diff --git a/x/ccv/provider/migrations/v6/legacy_params.go b/x/ccv/provider/migrations/v7/legacy_params.go similarity index 92% rename from x/ccv/provider/migrations/v6/legacy_params.go rename to x/ccv/provider/migrations/v7/legacy_params.go index 1841497bce..a8c6d5218d 100644 --- a/x/ccv/provider/migrations/v6/legacy_params.go +++ b/x/ccv/provider/migrations/v7/legacy_params.go @@ -1,4 +1,4 @@ -package v6 +package v7 import ( "time" @@ -75,6 +75,12 @@ func getBlocksPerEpoch(ctx sdk.Context, paramSpace ccvtypes.LegacyParamSubspace) return b } +func getNumberOfEpochsToStartReceivingRewards(ctx sdk.Context, paramSpace ccvtypes.LegacyParamSubspace) int64 { + var b int64 + paramSpace.Get(ctx, types.KeyNumberOfEpochsToStartReceivingRewards, &b) + return b +} + // Legacy: Only for migration purposes. GetParamsLegacy returns the paramset for the provider // module from a given param subspace func GetParamsLegacy(ctx sdk.Context, paramspace ccvtypes.LegacyParamSubspace) types.Params { @@ -88,5 +94,6 @@ func GetParamsLegacy(ctx sdk.Context, paramspace ccvtypes.LegacyParamSubspace) t getSlashMeterReplenishFraction(ctx, paramspace), getConsumerRewardDenomRegistrationFee(ctx, paramspace), getBlocksPerEpoch(ctx, paramspace), + getNumberOfEpochsToStartReceivingRewards(ctx, paramspace), ) } diff --git a/x/ccv/provider/migrations/v7/migrations.go b/x/ccv/provider/migrations/v7/migrations.go new file mode 100644 index 0000000000..3c2f171dac --- /dev/null +++ b/x/ccv/provider/migrations/v7/migrations.go @@ -0,0 +1,21 @@ +package v7 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" +) + +// MigrateParams migrates the provider module's parameters from the x/params to self store. +func MigrateLegacyParams(ctx sdk.Context, keeper providerkeeper.Keeper, legacyParamspace ccvtypes.LegacyParamSubspace) error { + ctx.Logger().Info("starting provider legacy params migration") + params := GetParamsLegacy(ctx, legacyParamspace) + err := params.Validate() + if err != nil { + return err + } + + keeper.SetParams(ctx, params) + keeper.Logger(ctx).Info("successfully migrated legacy provider parameters") + return nil +} diff --git a/x/ccv/provider/migrations/v7/migrations_test.go b/x/ccv/provider/migrations/v7/migrations_test.go new file mode 100644 index 0000000000..f4ee763263 --- /dev/null +++ b/x/ccv/provider/migrations/v7/migrations_test.go @@ -0,0 +1,56 @@ +package v7 + +import ( + "testing" + + testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/stretchr/testify/require" +) + +func TestMigrateParams(t *testing.T) { + t.Helper() + inMemParams := testutil.NewInMemKeeperParams(t) + k, ctx, ctrl, _ := testutil.GetProviderKeeperAndCtx(t, inMemParams) + defer ctrl.Finish() + + if !inMemParams.ParamsSubspace.HasKeyTable() { + inMemParams.ParamsSubspace.WithKeyTable(providertypes.ParamKeyTable()) + } + + defaultParams := providertypes.DefaultParams() + inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyTemplateClient, defaultParams.TemplateClient) + inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyTrustingPeriodFraction, defaultParams.TrustingPeriodFraction) + inMemParams.ParamsSubspace.Set(ctx, ccvtypes.KeyCCVTimeoutPeriod, defaultParams.CcvTimeoutPeriod) + inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyInitTimeoutPeriod, defaultParams.InitTimeoutPeriod) + inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyVscTimeoutPeriod, defaultParams.VscTimeoutPeriod) + inMemParams.ParamsSubspace.Set(ctx, providertypes.KeySlashMeterReplenishPeriod, defaultParams.SlashMeterReplenishPeriod) + inMemParams.ParamsSubspace.Set(ctx, providertypes.KeySlashMeterReplenishFraction, defaultParams.SlashMeterReplenishFraction) + inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyConsumerRewardDenomRegistrationFee, defaultParams.ConsumerRewardDenomRegistrationFee) + inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyBlocksPerEpoch, defaultParams.BlocksPerEpoch) + inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards, defaultParams.NumberOfEpochsToStartReceivingRewards) + + // confirms that inMemParams.ParamsSubspace works as expected + require.NotPanics(t, func() { + GetParamsLegacy(ctx, inMemParams.ParamsSubspace) + }) + + // no "new" params should be available before migration + // "new" params are stored under providertypes.ParametersKey() + emptyParams := k.GetParams(ctx) + require.Empty(t, emptyParams) + + // make sure that the legacy params are equal to the default params (they were set using inMemParams.ParamsSubspace.Set()) + legacyParams := GetParamsLegacy(ctx, inMemParams.ParamsSubspace) + require.NotNil(t, legacyParams) + require.Equal(t, defaultParams, legacyParams) + + err := MigrateLegacyParams(ctx, k, inMemParams.ParamsSubspace) + require.NoError(t, err) + + // check that "new" params are available after migration and equal to defaults + migratedParams := k.GetParams(ctx) + require.NotEmpty(t, migratedParams) + require.Equal(t, defaultParams, migratedParams) +} diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index d670ee2828..8c87262640 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -134,6 +134,9 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(providertypes.ModuleName, 5, migrator.Migrate5to6); err != nil { panic(fmt.Sprintf("failed to register migrator for %s: %s", providertypes.ModuleName, err)) } + if err := cfg.RegisterMigration(providertypes.ModuleName, 6, migrator.Migrate6to7); err != nil { + panic(fmt.Sprintf("failed to register migrator for %s: %s", providertypes.ModuleName, err)) + } } // InitGenesis performs genesis initialization for the provider module. It returns no validator updates. @@ -154,7 +157,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 6 } +func (AppModule) ConsensusVersion() uint64 { return 7 } // BeginBlock implements the AppModule interface func (am AppModule) BeginBlock(ctx context.Context) error { diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index f040a2ee08..6a9e5a7593 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -82,7 +82,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -103,7 +103,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -124,7 +124,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -145,7 +145,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -172,7 +172,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -199,7 +199,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -226,7 +226,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(1000000)}, 600), + sdk.Coin{Denom: "stake", Amount: math.NewInt(1000000)}, 600, 24), nil, nil, nil, @@ -253,7 +253,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -280,7 +280,7 @@ func TestValidateGenesisState(t *testing.T) { 0, // 0 vsc timeout here types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -307,7 +307,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, 0, // 0 slash meter replenish period here types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -334,7 +334,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, "1.15", - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -686,7 +686,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: math.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: math.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -707,7 +707,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(-1000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(-1000000)}, 600, 24), nil, nil, nil, diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index ac677004bc..81d69cd7eb 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -42,6 +42,17 @@ const ( // an epoch corresponds to 1 hour (6 * 600 = 3600 seconds). // forcing int64 as the Params KeyTable expects an int64 and not int. DefaultBlocksPerEpoch = int64(600) + + // DefaultNumberOfEpochsToStartReceivingRewards defines the default minimum number of epochs required by a validator to validate + // during so that the validator can start receiving rewards. This would mean that a validator has to be a consumer validator for at least + // `DefaultNumberOfEpochsToStartReceivingRewards * DefaultBlocksPerEpoch` on a consumer chain to start receiving rewards from the chain. + // Note that after a validator starts receiving rewards, the validator would keep receiving rewards every time the + // consumer chain sends an IBC transfer over to the provider. This value only sets a constraint on when a validator + // can first start receiving rewards to avoid cases where a validator just opts in to receive rewards and then opts out + // immediately afterward. + // Current default values for blocks per epoch corresponds to about 1 hour, so with 24 being the + // minimum amount of epochs, this would imply that a validator has to validate at least for 1 day to receive rewards. + DefaultNumberOfEpochsToStartReceivingRewards = int64(24) ) // Reflection based keys for params subspace @@ -49,14 +60,15 @@ const ( // Use x/ccv/provider/keeper/params instead // [DEPRECATED] var ( - KeyTemplateClient = []byte("TemplateClient") - KeyTrustingPeriodFraction = []byte("TrustingPeriodFraction") - KeyInitTimeoutPeriod = []byte("InitTimeoutPeriod") - KeyVscTimeoutPeriod = []byte("VscTimeoutPeriod") - KeySlashMeterReplenishPeriod = []byte("SlashMeterReplenishPeriod") - KeySlashMeterReplenishFraction = []byte("SlashMeterReplenishFraction") - KeyConsumerRewardDenomRegistrationFee = []byte("ConsumerRewardDenomRegistrationFee") - KeyBlocksPerEpoch = []byte("BlocksPerEpoch") + KeyTemplateClient = []byte("TemplateClient") + KeyTrustingPeriodFraction = []byte("TrustingPeriodFraction") + KeyInitTimeoutPeriod = []byte("InitTimeoutPeriod") + KeyVscTimeoutPeriod = []byte("VscTimeoutPeriod") + KeySlashMeterReplenishPeriod = []byte("SlashMeterReplenishPeriod") + KeySlashMeterReplenishFraction = []byte("SlashMeterReplenishFraction") + KeyConsumerRewardDenomRegistrationFee = []byte("ConsumerRewardDenomRegistrationFee") + KeyBlocksPerEpoch = []byte("BlocksPerEpoch") + KeyNumberOfEpochsToStartReceivingRewards = []byte("NumberOfEpochsToStartReceivingRewards") ) // ParamKeyTable returns a key table with the necessary registered provider params @@ -75,17 +87,19 @@ func NewParams( slashMeterReplenishFraction string, consumerRewardDenomRegistrationFee sdk.Coin, blocksPerEpoch int64, + numberOfEpochsToStartReceivingRewards int64, ) Params { return Params{ - TemplateClient: cs, - TrustingPeriodFraction: trustingPeriodFraction, - CcvTimeoutPeriod: ccvTimeoutPeriod, - InitTimeoutPeriod: initTimeoutPeriod, - VscTimeoutPeriod: vscTimeoutPeriod, - SlashMeterReplenishPeriod: slashMeterReplenishPeriod, - SlashMeterReplenishFraction: slashMeterReplenishFraction, - ConsumerRewardDenomRegistrationFee: consumerRewardDenomRegistrationFee, - BlocksPerEpoch: blocksPerEpoch, + TemplateClient: cs, + TrustingPeriodFraction: trustingPeriodFraction, + CcvTimeoutPeriod: ccvTimeoutPeriod, + InitTimeoutPeriod: initTimeoutPeriod, + VscTimeoutPeriod: vscTimeoutPeriod, + SlashMeterReplenishPeriod: slashMeterReplenishPeriod, + SlashMeterReplenishFraction: slashMeterReplenishFraction, + ConsumerRewardDenomRegistrationFee: consumerRewardDenomRegistrationFee, + BlocksPerEpoch: blocksPerEpoch, + NumberOfEpochsToStartReceivingRewards: numberOfEpochsToStartReceivingRewards, } } @@ -117,6 +131,7 @@ func DefaultParams() Params { Amount: math.NewInt(10000000), }, DefaultBlocksPerEpoch, + DefaultNumberOfEpochsToStartReceivingRewards, ) } @@ -149,9 +164,12 @@ func (p Params) Validate() error { if err := ValidateCoin(p.ConsumerRewardDenomRegistrationFee); err != nil { return fmt.Errorf("consumer reward denom registration fee is invalid: %s", err) } - if err := ccvtypes.ValidateInt64(p.BlocksPerEpoch); err != nil { + if err := ccvtypes.ValidatePositiveInt64(p.BlocksPerEpoch); err != nil { return fmt.Errorf("blocks per epoch is invalid: %s", err) } + if err := ccvtypes.ValidatePositiveInt64(p.NumberOfEpochsToStartReceivingRewards); err != nil { + return fmt.Errorf("number of epochs to start receiving rewards is invalid: %s", err) + } return nil } @@ -167,6 +185,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { paramtypes.NewParamSetPair(KeySlashMeterReplenishFraction, p.SlashMeterReplenishFraction, ccvtypes.ValidateStringFraction), paramtypes.NewParamSetPair(KeyConsumerRewardDenomRegistrationFee, p.ConsumerRewardDenomRegistrationFee, ValidateCoin), paramtypes.NewParamSetPair(KeyBlocksPerEpoch, p.BlocksPerEpoch, ccvtypes.ValidatePositiveInt64), + paramtypes.NewParamSetPair(KeyNumberOfEpochsToStartReceivingRewards, p.NumberOfEpochsToStartReceivingRewards, ccvtypes.ValidatePositiveInt64), } } diff --git a/x/ccv/provider/types/params_test.go b/x/ccv/provider/types/params_test.go index 675ddd0bc5..8ff8da58f1 100644 --- a/x/ccv/provider/types/params_test.go +++ b/x/ccv/provider/types/params_test.go @@ -25,39 +25,43 @@ func TestValidateParams(t *testing.T) { {"custom valid params", types.NewParams( ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), true}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), true}, {"custom invalid params", types.NewParams( ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, 0, clienttypes.Height{}, nil, []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), false}, {"blank client", types.NewParams(&ibctmtypes.ClientState{}, - "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), false}, - {"nil client", types.NewParams(nil, "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), false}, + {"nil client", types.NewParams(nil, "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), false}, // Check if "0.00" is valid or if a zero dec TrustFraction needs to return an error {"0 trusting period fraction", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), true}, + "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), true}, {"0 ccv timeout period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", 0, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), false}, + "0.33", 0, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), false}, {"0 init timeout period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, 0, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, 0, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), false}, {"0 vsc timeout period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 0, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 0, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), false}, {"0 slash meter replenish period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, 0, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, 0, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), false}, {"slash meter replenish fraction over 1", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "1.5", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "1.5", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24), false}, {"invalid consumer reward denom registration fee denom", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: math.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: math.NewInt(10000000)}, 1000, 24), false}, {"invalid consumer reward denom registration fee amount", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(-10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(-10000000)}, 1000, 24), false}, + {"invalid number of epochs to start receiving rewards", types.NewParams( + ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, + time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 0), false}, } for _, tc := range testCases { diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index dbada484bc..e602595797 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -597,6 +597,8 @@ type Params struct { ConsumerRewardDenomRegistrationFee types2.Coin `protobuf:"bytes,9,opt,name=consumer_reward_denom_registration_fee,json=consumerRewardDenomRegistrationFee,proto3" json:"consumer_reward_denom_registration_fee"` // The number of blocks that comprise an epoch. BlocksPerEpoch int64 `protobuf:"varint,10,opt,name=blocks_per_epoch,json=blocksPerEpoch,proto3" json:"blocks_per_epoch,omitempty"` + // The number of epochs a validator has to validate a consumer chain in order to start receiving rewards from that chain. + NumberOfEpochsToStartReceivingRewards int64 `protobuf:"varint,11,opt,name=number_of_epochs_to_start_receiving_rewards,json=numberOfEpochsToStartReceivingRewards,proto3" json:"number_of_epochs_to_start_receiving_rewards,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -695,6 +697,13 @@ func (m *Params) GetBlocksPerEpoch() int64 { return 0 } +func (m *Params) GetNumberOfEpochsToStartReceivingRewards() int64 { + if m != nil { + return m.NumberOfEpochsToStartReceivingRewards + } + return 0 +} + // SlashAcks contains cons addresses of consumer chain validators // successfully slashed on the provider chain. type SlashAcks struct { @@ -1547,6 +1556,11 @@ type ConsumerValidator struct { Power int64 `protobuf:"varint,2,opt,name=power,proto3" json:"power,omitempty"` // public key the validator uses on the consumer chain during this epoch ConsumerPublicKey *crypto.PublicKey `protobuf:"bytes,3,opt,name=consumer_public_key,json=consumerPublicKey,proto3" json:"consumer_public_key,omitempty"` + // height the validator had when it FIRST became a consumer validator + // If a validator becomes a consumer validator at height `H` and is continuously a consumer validator for all the upcoming + // epochs, then the height of the validator SHOULD remain `H`. This height only resets to a different height if a validator + // stops being a consumer validator during an epoch and later becomes again a consumer validator. + JoinHeight int64 `protobuf:"varint,4,opt,name=join_height,json=joinHeight,proto3" json:"join_height,omitempty"` } func (m *ConsumerValidator) Reset() { *m = ConsumerValidator{} } @@ -1603,6 +1617,13 @@ func (m *ConsumerValidator) GetConsumerPublicKey() *crypto.PublicKey { return nil } +func (m *ConsumerValidator) GetJoinHeight() int64 { + if m != nil { + return m.JoinHeight + } + return 0 +} + // 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. @@ -1683,132 +1704,136 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1988 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcb, 0x6f, 0x1b, 0xc7, - 0x19, 0xd7, 0x92, 0x94, 0x44, 0x0e, 0xf5, 0x1c, 0x29, 0xf1, 0x4a, 0x55, 0x29, 0x7a, 0xd3, 0xb8, - 0x6a, 0x5c, 0x2f, 0x23, 0x07, 0x01, 0x0c, 0xa1, 0x41, 0x20, 0x51, 0x4e, 0x2c, 0x2b, 0xb6, 0x95, - 0x95, 0x2a, 0xa3, 0xed, 0x61, 0x31, 0x9c, 0x1d, 0x93, 0x03, 0x2d, 0x77, 0xd6, 0x33, 0xc3, 0x55, - 0x78, 0xe9, 0xb9, 0x97, 0x02, 0xe9, 0x2d, 0xe8, 0xa1, 0x4d, 0x0b, 0x14, 0x28, 0x72, 0xea, 0xa1, - 0x7f, 0x41, 0x4f, 0x41, 0x2f, 0xcd, 0xb1, 0xa7, 0xa4, 0xb0, 0x0f, 0x3d, 0xf4, 0x9f, 0x28, 0x66, - 0xf6, 0x49, 0x4a, 0xb2, 0x69, 0x24, 0xb9, 0x48, 0xbb, 0xdf, 0xe3, 0xf7, 0x7d, 0x33, 0xdf, 0x93, - 0x0b, 0x6e, 0xd3, 0x40, 0x12, 0x8e, 0x7b, 0x88, 0x06, 0xae, 0x20, 0x78, 0xc0, 0xa9, 0x1c, 0xb6, - 0x30, 0x8e, 0x5a, 0x21, 0x67, 0x11, 0xf5, 0x08, 0x6f, 0x45, 0xdb, 0xd9, 0xb3, 0x1d, 0x72, 0x26, - 0x19, 0x7c, 0xe3, 0x12, 0x1d, 0x1b, 0xe3, 0xc8, 0xce, 0xe4, 0xa2, 0xed, 0xf5, 0x37, 0xaf, 0x02, - 0x8e, 0xb6, 0x5b, 0xe7, 0x94, 0x93, 0x18, 0x6b, 0x7d, 0xb5, 0xcb, 0xba, 0x4c, 0x3f, 0xb6, 0xd4, - 0x53, 0x42, 0xdd, 0xec, 0x32, 0xd6, 0xf5, 0x49, 0x4b, 0xbf, 0x75, 0x06, 0x4f, 0x5a, 0x92, 0xf6, - 0x89, 0x90, 0xa8, 0x1f, 0x26, 0x02, 0x8d, 0x71, 0x01, 0x6f, 0xc0, 0x91, 0xa4, 0x2c, 0x48, 0x01, - 0x68, 0x07, 0xb7, 0x30, 0xe3, 0xa4, 0x85, 0x7d, 0x4a, 0x02, 0xa9, 0xac, 0xc6, 0x4f, 0x89, 0x40, - 0x4b, 0x09, 0xf8, 0xb4, 0xdb, 0x93, 0x31, 0x59, 0xb4, 0x24, 0x09, 0x3c, 0xc2, 0xfb, 0x34, 0x16, - 0xce, 0xdf, 0x12, 0x85, 0x8d, 0x02, 0x1f, 0xf3, 0x61, 0x28, 0x59, 0xeb, 0x8c, 0x0c, 0x45, 0xc2, - 0xbd, 0x81, 0x99, 0xe8, 0x33, 0xd1, 0x22, 0xea, 0xfc, 0x01, 0x26, 0xad, 0x68, 0xbb, 0x43, 0x24, - 0xda, 0xce, 0x08, 0xa9, 0xdf, 0x89, 0x5c, 0x07, 0x89, 0x5c, 0x06, 0x33, 0x9a, 0xfa, 0xbd, 0x16, - 0xf3, 0xdd, 0xf8, 0x46, 0xe2, 0x97, 0x84, 0xb5, 0x8c, 0xfa, 0x34, 0x60, 0x2d, 0xfd, 0x37, 0x26, - 0x59, 0x5f, 0x54, 0x81, 0xd9, 0x66, 0x81, 0x18, 0xf4, 0x09, 0xdf, 0xf5, 0x3c, 0xaa, 0x2e, 0xe0, - 0x88, 0xb3, 0x90, 0x09, 0xe4, 0xc3, 0x55, 0x30, 0x2d, 0xa9, 0xf4, 0x89, 0x69, 0x34, 0x8d, 0xad, - 0x9a, 0x13, 0xbf, 0xc0, 0x26, 0xa8, 0x7b, 0x44, 0x60, 0x4e, 0x43, 0x25, 0x6c, 0x96, 0x34, 0xaf, - 0x48, 0x82, 0x6b, 0xa0, 0x1a, 0x47, 0x8d, 0x7a, 0x66, 0x59, 0xb3, 0x67, 0xf5, 0xfb, 0x81, 0x07, - 0x3f, 0x04, 0x0b, 0x34, 0xa0, 0x92, 0x22, 0xdf, 0xed, 0x11, 0x75, 0x77, 0x66, 0xa5, 0x69, 0x6c, - 0xd5, 0x6f, 0xaf, 0xdb, 0xb4, 0x83, 0x6d, 0x75, 0xdd, 0x76, 0x72, 0xc9, 0xd1, 0xb6, 0x7d, 0x4f, - 0x4b, 0xec, 0x55, 0xbe, 0xfc, 0x7a, 0x73, 0xca, 0x99, 0x4f, 0xf4, 0x62, 0x22, 0xbc, 0x0e, 0xe6, - 0xba, 0x24, 0x20, 0x82, 0x0a, 0xb7, 0x87, 0x44, 0xcf, 0x9c, 0x6e, 0x1a, 0x5b, 0x73, 0x4e, 0x3d, - 0xa1, 0xdd, 0x43, 0xa2, 0x07, 0x37, 0x41, 0xbd, 0x43, 0x03, 0xc4, 0x87, 0xb1, 0xc4, 0x8c, 0x96, - 0x00, 0x31, 0x49, 0x0b, 0xb4, 0x01, 0x10, 0x21, 0x3a, 0x0f, 0x5c, 0x95, 0x1b, 0xe6, 0x6c, 0xe2, - 0x48, 0x9c, 0x17, 0x76, 0x9a, 0x17, 0xf6, 0x49, 0x9a, 0x38, 0x7b, 0x55, 0xe5, 0xc8, 0xa7, 0xdf, - 0x6c, 0x1a, 0x4e, 0x4d, 0xeb, 0x29, 0x0e, 0x7c, 0x08, 0x96, 0x06, 0x41, 0x87, 0x05, 0x1e, 0x0d, - 0xba, 0x6e, 0x48, 0x38, 0x65, 0x9e, 0x59, 0xd5, 0x50, 0x6b, 0x17, 0xa0, 0xf6, 0x93, 0x14, 0x8b, - 0x91, 0x3e, 0x53, 0x48, 0x8b, 0x99, 0xf2, 0x91, 0xd6, 0x85, 0x1f, 0x03, 0x88, 0x71, 0xa4, 0x5d, - 0x62, 0x03, 0x99, 0x22, 0xd6, 0x26, 0x47, 0x5c, 0xc2, 0x38, 0x3a, 0x89, 0xb5, 0x13, 0xc8, 0x5f, - 0x81, 0x6b, 0x92, 0xa3, 0x40, 0x3c, 0x21, 0x7c, 0x1c, 0x17, 0x4c, 0x8e, 0xfb, 0x5a, 0x8a, 0x31, - 0x0a, 0x7e, 0x0f, 0x34, 0x71, 0x92, 0x40, 0x2e, 0x27, 0x1e, 0x15, 0x92, 0xd3, 0xce, 0x40, 0xe9, - 0xba, 0x4f, 0x38, 0xc2, 0x3a, 0x47, 0xea, 0x3a, 0x09, 0x1a, 0xa9, 0x9c, 0x33, 0x22, 0xf6, 0x41, - 0x22, 0x05, 0x1f, 0x81, 0x1f, 0x75, 0x7c, 0x86, 0xcf, 0x84, 0x72, 0xce, 0x1d, 0x41, 0xd2, 0xa6, - 0xfb, 0x54, 0x08, 0x85, 0x36, 0xd7, 0x34, 0xb6, 0xca, 0xce, 0xf5, 0x58, 0xf6, 0x88, 0xf0, 0xfd, - 0x82, 0xe4, 0x49, 0x41, 0x10, 0xde, 0x02, 0xb0, 0x47, 0x85, 0x64, 0x9c, 0x62, 0xe4, 0xbb, 0x24, - 0x90, 0x9c, 0x12, 0x61, 0xce, 0x6b, 0xf5, 0xe5, 0x9c, 0x73, 0x37, 0x66, 0xc0, 0xfb, 0xe0, 0xfa, - 0x95, 0x46, 0x5d, 0xdc, 0x43, 0x41, 0x40, 0x7c, 0x73, 0x41, 0x1f, 0x65, 0xd3, 0xbb, 0xc2, 0x66, - 0x3b, 0x16, 0x83, 0x2b, 0x60, 0x5a, 0xb2, 0xd0, 0x7d, 0x68, 0x2e, 0x36, 0x8d, 0xad, 0x79, 0xa7, - 0x22, 0x59, 0xf8, 0x10, 0xbe, 0x0d, 0x56, 0x23, 0xe4, 0x53, 0x0f, 0x49, 0xc6, 0x85, 0x1b, 0xb2, - 0x73, 0xc2, 0x5d, 0x8c, 0x42, 0x73, 0x49, 0xcb, 0xc0, 0x9c, 0x77, 0xa4, 0x58, 0x6d, 0x14, 0xc2, - 0xb7, 0xc0, 0x72, 0x46, 0x75, 0x05, 0x91, 0x5a, 0x7c, 0x59, 0x8b, 0x2f, 0x66, 0x8c, 0x63, 0x22, - 0x95, 0xec, 0x06, 0xa8, 0x21, 0xdf, 0x67, 0xe7, 0x3e, 0x15, 0xd2, 0x84, 0xcd, 0xf2, 0x56, 0xcd, - 0xc9, 0x09, 0x70, 0x1d, 0x54, 0x3d, 0x12, 0x0c, 0x35, 0x73, 0x45, 0x33, 0xb3, 0xf7, 0x9d, 0x1b, - 0xbf, 0xf9, 0x7c, 0x73, 0xea, 0xb3, 0xcf, 0x37, 0xa7, 0xfe, 0xf9, 0xf7, 0x5b, 0xeb, 0x49, 0xc7, - 0xe8, 0xb2, 0xc8, 0x4e, 0xba, 0x8b, 0xdd, 0x66, 0x81, 0x24, 0x81, 0xb4, 0xfe, 0x65, 0x80, 0x6b, - 0xed, 0x2c, 0x86, 0x7d, 0x16, 0x21, 0xff, 0xfb, 0xec, 0x15, 0xbb, 0xa0, 0x26, 0xd4, 0x25, 0xea, - 0xea, 0xac, 0xbc, 0x42, 0x75, 0x56, 0x95, 0x9a, 0x62, 0xec, 0x34, 0x5e, 0x72, 0xa2, 0x3f, 0x94, - 0xc0, 0x46, 0x7a, 0xa2, 0x07, 0xcc, 0xa3, 0x4f, 0x28, 0x46, 0xdf, 0x77, 0x0b, 0xcc, 0x52, 0xa3, - 0x32, 0x41, 0x6a, 0x4c, 0xbf, 0x5a, 0x6a, 0xcc, 0x4c, 0x90, 0x1a, 0xb3, 0x2f, 0x4a, 0x8d, 0xea, - 0x68, 0x6a, 0x58, 0x7f, 0x34, 0xc0, 0xea, 0xdd, 0xa7, 0x03, 0x1a, 0xb1, 0xef, 0xe8, 0x62, 0x0e, - 0xc1, 0x3c, 0x29, 0xe0, 0x09, 0xb3, 0xdc, 0x2c, 0x6f, 0xd5, 0x6f, 0xbf, 0x69, 0x27, 0x51, 0xca, - 0xa6, 0x5d, 0x1a, 0xaa, 0xa2, 0x75, 0x67, 0x54, 0x77, 0xa7, 0x64, 0x1a, 0xd6, 0x3f, 0x0c, 0xb0, - 0xae, 0xaa, 0xae, 0x4b, 0x1c, 0x72, 0x8e, 0xb8, 0xb7, 0x4f, 0x02, 0xd6, 0x17, 0xdf, 0xda, 0x4f, - 0x0b, 0xcc, 0x7b, 0x1a, 0xc9, 0x95, 0xcc, 0x45, 0x9e, 0xa7, 0xfd, 0xd4, 0x32, 0x8a, 0x78, 0xc2, - 0x76, 0x3d, 0x0f, 0x6e, 0x81, 0xa5, 0x5c, 0x86, 0xab, 0x82, 0x50, 0x79, 0xaa, 0xc4, 0x16, 0x52, - 0x31, 0x5d, 0x26, 0x2f, 0xcf, 0xc3, 0xff, 0x19, 0x60, 0xe9, 0x43, 0x9f, 0x75, 0x90, 0x7f, 0xec, - 0x23, 0xd1, 0x53, 0x1d, 0x69, 0xa8, 0xf2, 0x9f, 0x93, 0x64, 0x14, 0x68, 0xf7, 0x27, 0xce, 0x7f, - 0xa5, 0xa6, 0x87, 0xd3, 0xfb, 0x60, 0x39, 0x6b, 0xce, 0x59, 0x3e, 0xea, 0xd3, 0xee, 0xad, 0x3c, - 0xfb, 0x7a, 0x73, 0x31, 0xcd, 0xfd, 0xb6, 0xce, 0xcd, 0x7d, 0x67, 0x11, 0x8f, 0x10, 0x3c, 0xd8, - 0x00, 0x75, 0xda, 0xc1, 0xae, 0x20, 0x4f, 0xdd, 0x60, 0xd0, 0xd7, 0xa9, 0x5c, 0x71, 0x6a, 0xb4, - 0x83, 0x8f, 0xc9, 0xd3, 0x87, 0x83, 0x3e, 0x7c, 0x07, 0xbc, 0x9e, 0xae, 0x6c, 0x6e, 0x84, 0x7c, - 0x57, 0xe9, 0xab, 0xeb, 0xe2, 0x3a, 0xbb, 0xe7, 0x9c, 0x95, 0x94, 0x7b, 0x8a, 0x7c, 0x65, 0x6c, - 0xd7, 0xf3, 0xb8, 0xf5, 0x7c, 0x1a, 0xcc, 0x1c, 0x21, 0x8e, 0xfa, 0x02, 0x9e, 0x80, 0x45, 0x49, - 0xfa, 0xa1, 0x8f, 0x24, 0x71, 0xe3, 0xc1, 0x9f, 0x9c, 0xf4, 0xa6, 0x5e, 0x08, 0x8a, 0xeb, 0x95, - 0x5d, 0x58, 0xa8, 0xa2, 0x6d, 0xbb, 0xad, 0xa9, 0xc7, 0x12, 0x49, 0xe2, 0x2c, 0xa4, 0x18, 0x31, - 0x11, 0xde, 0x01, 0xa6, 0xe4, 0x03, 0x21, 0xf3, 0x91, 0x9c, 0xcf, 0xa2, 0x38, 0xd6, 0xaf, 0xa7, - 0xfc, 0x78, 0x8a, 0x65, 0x33, 0xe8, 0xf2, 0xe9, 0x5b, 0xfe, 0x36, 0xd3, 0xf7, 0x18, 0xac, 0xa8, - 0xd5, 0x65, 0x1c, 0xb3, 0x32, 0x39, 0xe6, 0xb2, 0xd2, 0x1f, 0x05, 0xfd, 0x18, 0xc0, 0x48, 0xe0, - 0x71, 0xcc, 0xe9, 0x57, 0xf0, 0x33, 0x12, 0x78, 0x14, 0xd2, 0x03, 0x1b, 0x42, 0x25, 0x9f, 0xdb, - 0x27, 0x52, 0xcf, 0xf2, 0xd0, 0x27, 0x01, 0x15, 0xbd, 0x14, 0x7c, 0x66, 0x72, 0xf0, 0x35, 0x0d, - 0xf4, 0x40, 0xe1, 0x38, 0x29, 0x4c, 0x62, 0xa5, 0x0d, 0x1a, 0x97, 0x5b, 0xc9, 0x02, 0x34, 0xab, - 0x03, 0xf4, 0x83, 0x4b, 0x20, 0xb2, 0x28, 0x09, 0x70, 0xa3, 0xb0, 0x73, 0xa8, 0xaa, 0x77, 0x75, - 0xc1, 0xb9, 0x9c, 0x74, 0xd5, 0x60, 0x46, 0xf1, 0xfa, 0x41, 0x48, 0xb6, 0x37, 0x25, 0xb5, 0xa7, - 0x96, 0xe6, 0x42, 0xf1, 0xd1, 0x20, 0x59, 0x2e, 0xad, 0x7c, 0x35, 0xc9, 0x7a, 0x88, 0x53, 0xc0, - 0xfa, 0x80, 0x10, 0x55, 0xed, 0x85, 0xf5, 0x84, 0x84, 0x0c, 0xf7, 0xf4, 0xfa, 0x54, 0x76, 0x16, - 0xb2, 0x55, 0xe4, 0xae, 0xa2, 0xde, 0xaf, 0x54, 0xab, 0x4b, 0x35, 0xeb, 0x27, 0xa0, 0xa6, 0x8b, - 0x79, 0x17, 0x9f, 0x09, 0xdd, 0x81, 0x3d, 0x8f, 0x13, 0x21, 0x88, 0x30, 0x8d, 0xa4, 0x03, 0xa7, - 0x04, 0x4b, 0x82, 0xb5, 0xab, 0x96, 0x70, 0x01, 0x1f, 0x83, 0xd9, 0x90, 0xe8, 0x0d, 0x51, 0x2b, - 0xd6, 0x6f, 0xbf, 0x67, 0x4f, 0xf0, 0xeb, 0xc9, 0xbe, 0x0a, 0xd0, 0x49, 0xd1, 0x2c, 0x9e, 0xaf, - 0xfe, 0x63, 0xd3, 0x5c, 0xc0, 0xd3, 0x71, 0xa3, 0x3f, 0x7b, 0x25, 0xa3, 0x63, 0x78, 0xb9, 0xcd, - 0x9b, 0xa0, 0xbe, 0x1b, 0x1f, 0xfb, 0x23, 0x35, 0x7a, 0x2e, 0x5c, 0xcb, 0x5c, 0xf1, 0x5a, 0xee, - 0x83, 0x85, 0x64, 0x9f, 0x3a, 0x61, 0xba, 0x21, 0xc1, 0x1f, 0x02, 0x90, 0x2c, 0x62, 0xaa, 0x91, - 0xc5, 0x2d, 0xbd, 0x96, 0x50, 0x0e, 0xbc, 0x91, 0xa9, 0x5b, 0x1a, 0x99, 0xba, 0x96, 0x03, 0x16, - 0x4f, 0x05, 0xfe, 0x79, 0xba, 0x6c, 0x3f, 0x0a, 0x05, 0x7c, 0x0d, 0xcc, 0xa8, 0x1a, 0x4a, 0x80, - 0x2a, 0xce, 0x74, 0x24, 0xf0, 0x81, 0xee, 0xea, 0xf9, 0x42, 0xcf, 0x42, 0x97, 0x7a, 0xc2, 0x2c, - 0x35, 0xcb, 0x5b, 0x15, 0x67, 0x61, 0x90, 0xab, 0x1f, 0x78, 0xc2, 0xfa, 0x05, 0xa8, 0x17, 0x00, - 0xe1, 0x02, 0x28, 0x65, 0x58, 0x25, 0xea, 0xc1, 0x1d, 0xb0, 0x96, 0x03, 0x8d, 0xb6, 0xe1, 0x18, - 0xb1, 0xe6, 0x5c, 0xcb, 0x04, 0x46, 0x3a, 0xb1, 0xb0, 0x1e, 0x81, 0xd5, 0x83, 0xbc, 0xe8, 0xb3, - 0x26, 0x3f, 0x72, 0x42, 0x63, 0x74, 0xaf, 0xd8, 0x00, 0xb5, 0xec, 0x37, 0xae, 0x3e, 0x7d, 0xc5, - 0xc9, 0x09, 0x56, 0x1f, 0x2c, 0x9d, 0x0a, 0x7c, 0x4c, 0x02, 0x2f, 0x07, 0xbb, 0xe2, 0x02, 0xf6, - 0xc6, 0x81, 0x26, 0xfe, 0x55, 0x94, 0x9b, 0x63, 0x60, 0xed, 0xb4, 0xb8, 0x84, 0xe8, 0x01, 0x7d, - 0x84, 0xf0, 0x19, 0x91, 0x02, 0x3a, 0xa0, 0xa2, 0x97, 0x8d, 0x38, 0xb3, 0xee, 0x5c, 0x99, 0x59, - 0xd1, 0xb6, 0x7d, 0x15, 0xc8, 0x3e, 0x92, 0x28, 0xa9, 0x5d, 0x8d, 0x65, 0xfd, 0x18, 0xac, 0x3c, - 0x40, 0x72, 0xc0, 0x89, 0x37, 0x12, 0xe3, 0x25, 0x50, 0x56, 0xf1, 0x33, 0x74, 0xfc, 0xd4, 0xa3, - 0xf5, 0x67, 0x03, 0x98, 0x77, 0x3f, 0x09, 0x19, 0x97, 0xc4, 0xbb, 0x70, 0x23, 0x2f, 0xb8, 0xde, - 0x33, 0xb0, 0xa2, 0x2e, 0x4b, 0x90, 0xc0, 0x73, 0xb3, 0x73, 0xc6, 0x71, 0xac, 0xdf, 0x7e, 0x77, - 0xa2, 0xea, 0x18, 0x37, 0x97, 0x1c, 0x60, 0x39, 0x1a, 0xa3, 0x0b, 0xeb, 0x77, 0x06, 0x30, 0x0f, - 0xc9, 0x70, 0x57, 0x08, 0xda, 0x0d, 0xfa, 0x24, 0x90, 0xaa, 0x07, 0x22, 0x4c, 0xd4, 0x23, 0x7c, - 0x03, 0xcc, 0x67, 0x33, 0x57, 0x8f, 0x5a, 0x43, 0x8f, 0xda, 0xb9, 0x94, 0xa8, 0x0a, 0x0c, 0xee, - 0x00, 0x10, 0x72, 0x12, 0xb9, 0xd8, 0x3d, 0x23, 0xc3, 0x24, 0x8a, 0x1b, 0xc5, 0x11, 0x1a, 0x7f, - 0x81, 0xb0, 0x8f, 0x06, 0x1d, 0x9f, 0xe2, 0x43, 0x32, 0x74, 0xaa, 0x4a, 0xbe, 0x7d, 0x48, 0x86, - 0x6a, 0x67, 0xd2, 0x1b, 0xa8, 0x9e, 0x7b, 0x65, 0x27, 0x7e, 0xb1, 0x7e, 0x6f, 0x80, 0x6b, 0x59, - 0x38, 0xd2, 0x74, 0x3d, 0x1a, 0x74, 0x94, 0xc6, 0x0b, 0xee, 0xed, 0x82, 0xb7, 0xa5, 0x4b, 0xbc, - 0x7d, 0x1f, 0xcc, 0x65, 0x05, 0xa2, 0xfc, 0x2d, 0x4f, 0xe0, 0x6f, 0x3d, 0xd5, 0x38, 0x24, 0x43, - 0xeb, 0xd7, 0x05, 0xdf, 0xf6, 0x86, 0x85, 0xde, 0xc7, 0x5f, 0xe2, 0x5b, 0x66, 0xb6, 0xe8, 0x1b, - 0x2e, 0xea, 0x5f, 0x38, 0x40, 0xf9, 0xe2, 0x01, 0xac, 0x3f, 0x19, 0x60, 0xb5, 0x68, 0x55, 0x9c, - 0xb0, 0x23, 0x3e, 0x08, 0xc8, 0x8b, 0xac, 0xe7, 0xe5, 0x57, 0x2a, 0x96, 0xdf, 0x63, 0xb0, 0x30, - 0xe2, 0x94, 0x48, 0x6e, 0xe3, 0xed, 0x89, 0x72, 0xac, 0xd0, 0x5d, 0x9d, 0xf9, 0xe2, 0x39, 0x84, - 0xf5, 0x17, 0x03, 0x2c, 0xa7, 0x3e, 0x66, 0x97, 0x05, 0x7f, 0x0a, 0x60, 0x76, 0xbc, 0x7c, 0x7b, - 0x8b, 0x53, 0x6a, 0x29, 0xe5, 0xa4, 0xab, 0x5b, 0x9e, 0x1a, 0xa5, 0x42, 0x6a, 0xc0, 0x8f, 0xc0, - 0x4a, 0xe6, 0x72, 0xa8, 0x03, 0x34, 0x71, 0x14, 0xb3, 0xfd, 0x34, 0x23, 0x59, 0xbf, 0x35, 0xf2, - 0x71, 0x18, 0xcf, 0x63, 0xb1, 0xeb, 0xfb, 0xc9, 0xd2, 0x0f, 0x43, 0x30, 0x1b, 0x8f, 0x7c, 0x91, - 0xf4, 0x8f, 0x8d, 0x4b, 0x87, 0xfb, 0x3e, 0xc1, 0x7a, 0xbe, 0xdf, 0x51, 0x25, 0xf6, 0xc5, 0x37, - 0x9b, 0x37, 0xbb, 0x54, 0xf6, 0x06, 0x1d, 0x1b, 0xb3, 0x7e, 0xf2, 0x51, 0x2c, 0xf9, 0x77, 0x4b, - 0x78, 0x67, 0x2d, 0x39, 0x0c, 0x89, 0x48, 0x75, 0xc4, 0x5f, 0xff, 0xfb, 0xb7, 0xb7, 0x0c, 0x27, - 0x35, 0xb3, 0xf7, 0xf8, 0xcb, 0x67, 0x0d, 0xe3, 0xab, 0x67, 0x0d, 0xe3, 0x3f, 0xcf, 0x1a, 0xc6, - 0xa7, 0xcf, 0x1b, 0x53, 0x5f, 0x3d, 0x6f, 0x4c, 0xfd, 0xfb, 0x79, 0x63, 0xea, 0x97, 0xef, 0x5d, - 0x04, 0xcd, 0x63, 0x74, 0x2b, 0xfb, 0x66, 0x19, 0xbd, 0xdb, 0xfa, 0x64, 0xf4, 0x8b, 0xa8, 0xb6, - 0xd7, 0x99, 0xd1, 0xdd, 0xf4, 0x9d, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x62, 0x6f, 0xc0, 0x43, - 0x42, 0x15, 0x00, 0x00, + // 2052 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xbb, 0x6f, 0x1c, 0xc7, + 0x19, 0xe7, 0xf2, 0x8e, 0x8f, 0xfb, 0x8e, 0xcf, 0x21, 0x6d, 0x2d, 0x19, 0xe6, 0x48, 0xad, 0x23, + 0x85, 0xb1, 0xa2, 0x3b, 0x53, 0x86, 0x01, 0x41, 0x88, 0x61, 0xf0, 0x21, 0x5b, 0x0f, 0x4b, 0xa2, + 0x97, 0x8c, 0x84, 0x38, 0xc5, 0x62, 0x6e, 0x76, 0x78, 0x37, 0xe1, 0xde, 0xce, 0x6a, 0x66, 0x6e, + 0xe9, 0x6b, 0x52, 0xa7, 0x09, 0xe0, 0x74, 0x46, 0x8a, 0xc4, 0x49, 0x15, 0xb8, 0x4a, 0x91, 0x3e, + 0x40, 0x8a, 0xc0, 0x48, 0x13, 0x97, 0xa9, 0xec, 0x40, 0x2a, 0x52, 0xe4, 0x9f, 0x08, 0x66, 0xf6, + 0x79, 0x7c, 0x48, 0x27, 0x38, 0x6e, 0xc8, 0xdb, 0xef, 0xf1, 0xfb, 0xbe, 0x99, 0xef, 0xb9, 0x0b, + 0x37, 0x58, 0xa8, 0xa8, 0x20, 0x5d, 0xcc, 0x42, 0x4f, 0x52, 0xd2, 0x17, 0x4c, 0x0d, 0x5a, 0x84, + 0xc4, 0xad, 0x48, 0xf0, 0x98, 0xf9, 0x54, 0xb4, 0xe2, 0xad, 0xfc, 0x77, 0x33, 0x12, 0x5c, 0x71, + 0xf4, 0xc6, 0x39, 0x3a, 0x4d, 0x42, 0xe2, 0x66, 0x2e, 0x17, 0x6f, 0xad, 0x5e, 0xb9, 0x08, 0x38, + 0xde, 0x6a, 0x9d, 0x30, 0x41, 0x13, 0xac, 0xd5, 0xe5, 0x0e, 0xef, 0x70, 0xf3, 0xb3, 0xa5, 0x7f, + 0xa5, 0xd4, 0xf5, 0x0e, 0xe7, 0x9d, 0x80, 0xb6, 0xcc, 0x53, 0xbb, 0x7f, 0xd4, 0x52, 0xac, 0x47, + 0xa5, 0xc2, 0xbd, 0x28, 0x15, 0x68, 0x9c, 0x16, 0xf0, 0xfb, 0x02, 0x2b, 0xc6, 0xc3, 0x0c, 0x80, + 0xb5, 0x49, 0x8b, 0x70, 0x41, 0x5b, 0x24, 0x60, 0x34, 0x54, 0xda, 0x6a, 0xf2, 0x2b, 0x15, 0x68, + 0x69, 0x81, 0x80, 0x75, 0xba, 0x2a, 0x21, 0xcb, 0x96, 0xa2, 0xa1, 0x4f, 0x45, 0x8f, 0x25, 0xc2, + 0xc5, 0x53, 0xaa, 0xb0, 0x56, 0xe2, 0x13, 0x31, 0x88, 0x14, 0x6f, 0x1d, 0xd3, 0x81, 0x4c, 0xb9, + 0x57, 0x09, 0x97, 0x3d, 0x2e, 0x5b, 0x54, 0x9f, 0x3f, 0x24, 0xb4, 0x15, 0x6f, 0xb5, 0xa9, 0xc2, + 0x5b, 0x39, 0x21, 0xf3, 0x3b, 0x95, 0x6b, 0x63, 0x59, 0xc8, 0x10, 0xce, 0x32, 0xbf, 0x57, 0x12, + 0xbe, 0x97, 0xdc, 0x48, 0xf2, 0x90, 0xb2, 0x16, 0x71, 0x8f, 0x85, 0xbc, 0x65, 0xfe, 0x26, 0x24, + 0xe7, 0x8b, 0x69, 0xb0, 0x77, 0x79, 0x28, 0xfb, 0x3d, 0x2a, 0xb6, 0x7d, 0x9f, 0xe9, 0x0b, 0xd8, + 0x17, 0x3c, 0xe2, 0x12, 0x07, 0x68, 0x19, 0x26, 0x14, 0x53, 0x01, 0xb5, 0xad, 0x0d, 0x6b, 0xb3, + 0xe6, 0x26, 0x0f, 0x68, 0x03, 0xea, 0x3e, 0x95, 0x44, 0xb0, 0x48, 0x0b, 0xdb, 0xe3, 0x86, 0x57, + 0x26, 0xa1, 0x15, 0x98, 0x4e, 0xa2, 0xc6, 0x7c, 0xbb, 0x62, 0xd8, 0x53, 0xe6, 0xf9, 0xae, 0x8f, + 0x3e, 0x80, 0x39, 0x16, 0x32, 0xc5, 0x70, 0xe0, 0x75, 0xa9, 0xbe, 0x3b, 0xbb, 0xba, 0x61, 0x6d, + 0xd6, 0x6f, 0xac, 0x36, 0x59, 0x9b, 0x34, 0xf5, 0x75, 0x37, 0xd3, 0x4b, 0x8e, 0xb7, 0x9a, 0x77, + 0x8c, 0xc4, 0x4e, 0xf5, 0xcb, 0xaf, 0xd7, 0xc7, 0xdc, 0xd9, 0x54, 0x2f, 0x21, 0xa2, 0xcb, 0x30, + 0xd3, 0xa1, 0x21, 0x95, 0x4c, 0x7a, 0x5d, 0x2c, 0xbb, 0xf6, 0xc4, 0x86, 0xb5, 0x39, 0xe3, 0xd6, + 0x53, 0xda, 0x1d, 0x2c, 0xbb, 0x68, 0x1d, 0xea, 0x6d, 0x16, 0x62, 0x31, 0x48, 0x24, 0x26, 0x8d, + 0x04, 0x24, 0x24, 0x23, 0xb0, 0x0b, 0x20, 0x23, 0x7c, 0x12, 0x7a, 0x3a, 0x37, 0xec, 0xa9, 0xd4, + 0x91, 0x24, 0x2f, 0x9a, 0x59, 0x5e, 0x34, 0x0f, 0xb3, 0xc4, 0xd9, 0x99, 0xd6, 0x8e, 0x7c, 0xfa, + 0xcd, 0xba, 0xe5, 0xd6, 0x8c, 0x9e, 0xe6, 0xa0, 0x87, 0xb0, 0xd0, 0x0f, 0xdb, 0x3c, 0xf4, 0x59, + 0xd8, 0xf1, 0x22, 0x2a, 0x18, 0xf7, 0xed, 0x69, 0x03, 0xb5, 0x72, 0x06, 0x6a, 0x2f, 0x4d, 0xb1, + 0x04, 0xe9, 0x33, 0x8d, 0x34, 0x9f, 0x2b, 0xef, 0x1b, 0x5d, 0xf4, 0x11, 0x20, 0x42, 0x62, 0xe3, + 0x12, 0xef, 0xab, 0x0c, 0xb1, 0x36, 0x3a, 0xe2, 0x02, 0x21, 0xf1, 0x61, 0xa2, 0x9d, 0x42, 0xfe, + 0x1c, 0x2e, 0x29, 0x81, 0x43, 0x79, 0x44, 0xc5, 0x69, 0x5c, 0x18, 0x1d, 0xf7, 0xb5, 0x0c, 0x63, + 0x18, 0xfc, 0x0e, 0x6c, 0x90, 0x34, 0x81, 0x3c, 0x41, 0x7d, 0x26, 0x95, 0x60, 0xed, 0xbe, 0xd6, + 0xf5, 0x8e, 0x04, 0x26, 0x26, 0x47, 0xea, 0x26, 0x09, 0x1a, 0x99, 0x9c, 0x3b, 0x24, 0xf6, 0x7e, + 0x2a, 0x85, 0x1e, 0xc1, 0x0f, 0xda, 0x01, 0x27, 0xc7, 0x52, 0x3b, 0xe7, 0x0d, 0x21, 0x19, 0xd3, + 0x3d, 0x26, 0xa5, 0x46, 0x9b, 0xd9, 0xb0, 0x36, 0x2b, 0xee, 0xe5, 0x44, 0x76, 0x9f, 0x8a, 0xbd, + 0x92, 0xe4, 0x61, 0x49, 0x10, 0x5d, 0x07, 0xd4, 0x65, 0x52, 0x71, 0xc1, 0x08, 0x0e, 0x3c, 0x1a, + 0x2a, 0xc1, 0xa8, 0xb4, 0x67, 0x8d, 0xfa, 0x62, 0xc1, 0xb9, 0x9d, 0x30, 0xd0, 0x3d, 0xb8, 0x7c, + 0xa1, 0x51, 0x8f, 0x74, 0x71, 0x18, 0xd2, 0xc0, 0x9e, 0x33, 0x47, 0x59, 0xf7, 0x2f, 0xb0, 0xb9, + 0x9b, 0x88, 0xa1, 0x25, 0x98, 0x50, 0x3c, 0xf2, 0x1e, 0xda, 0xf3, 0x1b, 0xd6, 0xe6, 0xac, 0x5b, + 0x55, 0x3c, 0x7a, 0x88, 0xde, 0x82, 0xe5, 0x18, 0x07, 0xcc, 0xc7, 0x8a, 0x0b, 0xe9, 0x45, 0xfc, + 0x84, 0x0a, 0x8f, 0xe0, 0xc8, 0x5e, 0x30, 0x32, 0xa8, 0xe0, 0xed, 0x6b, 0xd6, 0x2e, 0x8e, 0xd0, + 0x9b, 0xb0, 0x98, 0x53, 0x3d, 0x49, 0x95, 0x11, 0x5f, 0x34, 0xe2, 0xf3, 0x39, 0xe3, 0x80, 0x2a, + 0x2d, 0xbb, 0x06, 0x35, 0x1c, 0x04, 0xfc, 0x24, 0x60, 0x52, 0xd9, 0x68, 0xa3, 0xb2, 0x59, 0x73, + 0x0b, 0x02, 0x5a, 0x85, 0x69, 0x9f, 0x86, 0x03, 0xc3, 0x5c, 0x32, 0xcc, 0xfc, 0xf9, 0xd6, 0xd5, + 0x5f, 0x7d, 0xbe, 0x3e, 0xf6, 0xd9, 0xe7, 0xeb, 0x63, 0xff, 0xf8, 0xcb, 0xf5, 0xd5, 0xb4, 0x63, + 0x74, 0x78, 0xdc, 0x4c, 0xbb, 0x4b, 0x73, 0x97, 0x87, 0x8a, 0x86, 0xca, 0xf9, 0xa7, 0x05, 0x97, + 0x76, 0xf3, 0x18, 0xf6, 0x78, 0x8c, 0x83, 0xef, 0xb2, 0x57, 0x6c, 0x43, 0x4d, 0xea, 0x4b, 0x34, + 0xd5, 0x59, 0x7d, 0x85, 0xea, 0x9c, 0xd6, 0x6a, 0x9a, 0x71, 0xab, 0xf1, 0x92, 0x13, 0xfd, 0x6e, + 0x1c, 0xd6, 0xb2, 0x13, 0x3d, 0xe0, 0x3e, 0x3b, 0x62, 0x04, 0x7f, 0xd7, 0x2d, 0x30, 0x4f, 0x8d, + 0xea, 0x08, 0xa9, 0x31, 0xf1, 0x6a, 0xa9, 0x31, 0x39, 0x42, 0x6a, 0x4c, 0xbd, 0x28, 0x35, 0xa6, + 0x87, 0x53, 0xc3, 0xf9, 0xbd, 0x05, 0xcb, 0xb7, 0x9f, 0xf6, 0x59, 0xcc, 0xff, 0x4f, 0x17, 0x73, + 0x1f, 0x66, 0x69, 0x09, 0x4f, 0xda, 0x95, 0x8d, 0xca, 0x66, 0xfd, 0xc6, 0x95, 0x66, 0x1a, 0xa5, + 0x7c, 0xda, 0x65, 0xa1, 0x2a, 0x5b, 0x77, 0x87, 0x75, 0x6f, 0x8d, 0xdb, 0x96, 0xf3, 0x37, 0x0b, + 0x56, 0x75, 0xd5, 0x75, 0xa8, 0x4b, 0x4f, 0xb0, 0xf0, 0xf7, 0x68, 0xc8, 0x7b, 0xf2, 0x5b, 0xfb, + 0xe9, 0xc0, 0xac, 0x6f, 0x90, 0x3c, 0xc5, 0x3d, 0xec, 0xfb, 0xc6, 0x4f, 0x23, 0xa3, 0x89, 0x87, + 0x7c, 0xdb, 0xf7, 0xd1, 0x26, 0x2c, 0x14, 0x32, 0x42, 0x17, 0x84, 0xce, 0x53, 0x2d, 0x36, 0x97, + 0x89, 0x99, 0x32, 0x79, 0x79, 0x1e, 0xfe, 0xd7, 0x82, 0x85, 0x0f, 0x02, 0xde, 0xc6, 0xc1, 0x41, + 0x80, 0x65, 0x57, 0x77, 0xa4, 0x81, 0xce, 0x7f, 0x41, 0xd3, 0x51, 0x60, 0xdc, 0x1f, 0x39, 0xff, + 0xb5, 0x9a, 0x19, 0x4e, 0xef, 0xc1, 0x62, 0xde, 0x9c, 0xf3, 0x7c, 0x34, 0xa7, 0xdd, 0x59, 0x7a, + 0xf6, 0xf5, 0xfa, 0x7c, 0x96, 0xfb, 0xbb, 0x26, 0x37, 0xf7, 0xdc, 0x79, 0x32, 0x44, 0xf0, 0x51, + 0x03, 0xea, 0xac, 0x4d, 0x3c, 0x49, 0x9f, 0x7a, 0x61, 0xbf, 0x67, 0x52, 0xb9, 0xea, 0xd6, 0x58, + 0x9b, 0x1c, 0xd0, 0xa7, 0x0f, 0xfb, 0x3d, 0xf4, 0x36, 0xbc, 0x9e, 0xad, 0x6c, 0x5e, 0x8c, 0x03, + 0x4f, 0xeb, 0xeb, 0xeb, 0x12, 0x26, 0xbb, 0x67, 0xdc, 0xa5, 0x8c, 0xfb, 0x18, 0x07, 0xda, 0xd8, + 0xb6, 0xef, 0x0b, 0xe7, 0xaf, 0x93, 0x30, 0xb9, 0x8f, 0x05, 0xee, 0x49, 0x74, 0x08, 0xf3, 0x8a, + 0xf6, 0xa2, 0x00, 0x2b, 0xea, 0x25, 0x83, 0x3f, 0x3d, 0xe9, 0x35, 0xb3, 0x10, 0x94, 0xd7, 0xab, + 0x66, 0x69, 0xa1, 0x8a, 0xb7, 0x9a, 0xbb, 0x86, 0x7a, 0xa0, 0xb0, 0xa2, 0xee, 0x5c, 0x86, 0x91, + 0x10, 0xd1, 0x4d, 0xb0, 0x95, 0xe8, 0x4b, 0x55, 0x8c, 0xe4, 0x62, 0x16, 0x25, 0xb1, 0x7e, 0x3d, + 0xe3, 0x27, 0x53, 0x2c, 0x9f, 0x41, 0xe7, 0x4f, 0xdf, 0xca, 0xb7, 0x99, 0xbe, 0x07, 0xb0, 0xa4, + 0x57, 0x97, 0xd3, 0x98, 0xd5, 0xd1, 0x31, 0x17, 0xb5, 0xfe, 0x30, 0xe8, 0x47, 0x80, 0x62, 0x49, + 0x4e, 0x63, 0x4e, 0xbc, 0x82, 0x9f, 0xb1, 0x24, 0xc3, 0x90, 0x3e, 0xac, 0x49, 0x9d, 0x7c, 0x5e, + 0x8f, 0x2a, 0x33, 0xcb, 0xa3, 0x80, 0x86, 0x4c, 0x76, 0x33, 0xf0, 0xc9, 0xd1, 0xc1, 0x57, 0x0c, + 0xd0, 0x03, 0x8d, 0xe3, 0x66, 0x30, 0xa9, 0x95, 0x5d, 0x68, 0x9c, 0x6f, 0x25, 0x0f, 0xd0, 0x94, + 0x09, 0xd0, 0xf7, 0xce, 0x81, 0xc8, 0xa3, 0x24, 0xe1, 0x6a, 0x69, 0xe7, 0xd0, 0x55, 0xef, 0x99, + 0x82, 0xf3, 0x04, 0xed, 0xe8, 0xc1, 0x8c, 0x93, 0xf5, 0x83, 0xd2, 0x7c, 0x6f, 0x4a, 0x6b, 0x4f, + 0x2f, 0xcd, 0xa5, 0xe2, 0x63, 0x61, 0xba, 0x5c, 0x3a, 0xc5, 0x6a, 0x92, 0xf7, 0x10, 0xb7, 0x84, + 0xf5, 0x3e, 0xa5, 0xba, 0xda, 0x4b, 0xeb, 0x09, 0x8d, 0x38, 0xe9, 0x9a, 0xf5, 0xa9, 0xe2, 0xce, + 0xe5, 0xab, 0xc8, 0x6d, 0x4d, 0x45, 0x1f, 0xc3, 0xb5, 0xb0, 0xdf, 0x6b, 0x53, 0xe1, 0xf1, 0xa3, + 0x44, 0xd0, 0x74, 0x08, 0xa9, 0xb0, 0x50, 0x9e, 0xa0, 0x84, 0xb2, 0x58, 0x67, 0x66, 0xe2, 0xb9, + 0x34, 0xdb, 0x51, 0xc5, 0xbd, 0x92, 0xa8, 0x3c, 0x3a, 0x32, 0x18, 0xf2, 0x90, 0x1f, 0x68, 0x71, + 0x37, 0x93, 0x4e, 0x1c, 0x93, 0xf7, 0xaa, 0xd3, 0xd3, 0x0b, 0x35, 0xe7, 0x47, 0x50, 0x33, 0x8d, + 0x62, 0x9b, 0x1c, 0x4b, 0xd3, 0xdd, 0x7d, 0x5f, 0x50, 0x29, 0xa9, 0xb4, 0xad, 0xb4, 0xbb, 0x67, + 0x04, 0x47, 0xc1, 0xca, 0x45, 0x0b, 0xbe, 0x44, 0x4f, 0x60, 0x2a, 0xa2, 0x66, 0xfb, 0x34, 0x8a, + 0xf5, 0x1b, 0xef, 0x36, 0x47, 0x78, 0x33, 0x6b, 0x5e, 0x04, 0xe8, 0x66, 0x68, 0x8e, 0x28, 0x5e, + 0x2b, 0x4e, 0x6d, 0x0a, 0x12, 0x3d, 0x3e, 0x6d, 0xf4, 0x27, 0xaf, 0x64, 0xf4, 0x14, 0x5e, 0x61, + 0xf3, 0x1a, 0xd4, 0xb7, 0x93, 0x63, 0x7f, 0xa8, 0xc7, 0xda, 0x99, 0x6b, 0x99, 0x29, 0x5f, 0xcb, + 0x3d, 0x98, 0x4b, 0x77, 0xb5, 0x43, 0x6e, 0x9a, 0x1d, 0xfa, 0x3e, 0x40, 0xba, 0xe4, 0xe9, 0x26, + 0x99, 0x8c, 0x8b, 0x5a, 0x4a, 0xb9, 0xeb, 0x0f, 0x4d, 0xf4, 0xf1, 0xa1, 0x89, 0xee, 0xb8, 0x30, + 0xff, 0x58, 0x92, 0x9f, 0x66, 0x8b, 0xfc, 0xa3, 0x48, 0xa2, 0xd7, 0x60, 0x52, 0xd7, 0x67, 0x0a, + 0x54, 0x75, 0x27, 0x62, 0x49, 0xee, 0x9a, 0x89, 0x51, 0xbc, 0x2c, 0xf0, 0xc8, 0x63, 0xbe, 0xb4, + 0xc7, 0x37, 0x2a, 0x9b, 0x55, 0x77, 0xae, 0x5f, 0xa8, 0xdf, 0xf5, 0xa5, 0xf3, 0x33, 0xa8, 0x97, + 0x00, 0xd1, 0x1c, 0x8c, 0xe7, 0x58, 0xe3, 0xcc, 0x47, 0xb7, 0x60, 0xa5, 0x00, 0x1a, 0x6e, 0xf1, + 0x09, 0x62, 0xcd, 0xbd, 0x94, 0x0b, 0x0c, 0x75, 0x79, 0xe9, 0x3c, 0x82, 0xe5, 0xbb, 0x45, 0x43, + 0xc9, 0x07, 0xc8, 0xd0, 0x09, 0xad, 0xe1, 0x9d, 0x65, 0x0d, 0x6a, 0xf9, 0xfb, 0xb3, 0x39, 0x7d, + 0xd5, 0x2d, 0x08, 0x4e, 0x0f, 0x16, 0x1e, 0x4b, 0x72, 0x40, 0x43, 0xbf, 0x00, 0xbb, 0xe0, 0x02, + 0x76, 0x4e, 0x03, 0x8d, 0xfc, 0xc6, 0x55, 0x98, 0xe3, 0xb0, 0xf2, 0xb8, 0xbc, 0xe0, 0x98, 0xe1, + 0xbf, 0x8f, 0xc9, 0x31, 0x55, 0x12, 0xb9, 0x50, 0x35, 0x8b, 0x4c, 0x92, 0x59, 0x37, 0x2f, 0xcc, + 0xac, 0x78, 0xab, 0x79, 0x11, 0xc8, 0x1e, 0x56, 0x38, 0xed, 0x0b, 0x06, 0xcb, 0xf9, 0x21, 0x2c, + 0x3d, 0xc0, 0xaa, 0x2f, 0xa8, 0x3f, 0x14, 0xe3, 0x05, 0xa8, 0xe8, 0xf8, 0x59, 0x26, 0x7e, 0xfa, + 0xa7, 0xf3, 0x47, 0x0b, 0xec, 0xdb, 0x9f, 0x44, 0x5c, 0x28, 0xea, 0x9f, 0xb9, 0x91, 0x17, 0x5c, + 0xef, 0x31, 0x2c, 0xe9, 0xcb, 0x92, 0x34, 0xf4, 0xbd, 0xfc, 0x9c, 0x49, 0x1c, 0xeb, 0x37, 0xde, + 0x19, 0xa9, 0x3a, 0x4e, 0x9b, 0x4b, 0x0f, 0xb0, 0x18, 0x9f, 0xa2, 0x4b, 0xe7, 0x37, 0x16, 0xd8, + 0xf7, 0xe9, 0x60, 0x5b, 0x4a, 0xd6, 0x09, 0x7b, 0x34, 0x54, 0xba, 0xbf, 0x62, 0x42, 0xf5, 0x4f, + 0xf4, 0x06, 0xcc, 0xe6, 0xf3, 0xdc, 0x8c, 0x71, 0xcb, 0x8c, 0xf1, 0x99, 0x8c, 0xa8, 0x0b, 0x0c, + 0xdd, 0x02, 0x88, 0x04, 0x8d, 0x3d, 0xe2, 0x1d, 0xd3, 0x41, 0x1a, 0xc5, 0xb5, 0xf2, 0x78, 0x4e, + 0xbe, 0x6e, 0x34, 0xf7, 0xfb, 0xed, 0x80, 0x91, 0xfb, 0x74, 0xe0, 0x4e, 0x6b, 0xf9, 0xdd, 0xfb, + 0x74, 0xa0, 0xf7, 0x31, 0xb3, 0xdd, 0x9a, 0x99, 0x5a, 0x71, 0x93, 0x07, 0xe7, 0xb7, 0x16, 0x5c, + 0xca, 0xc3, 0x91, 0xa5, 0xeb, 0x7e, 0xbf, 0xad, 0x35, 0x5e, 0x70, 0x6f, 0x67, 0xbc, 0x1d, 0x3f, + 0xc7, 0xdb, 0xf7, 0x60, 0x26, 0x2f, 0x10, 0xed, 0x6f, 0x65, 0x04, 0x7f, 0xeb, 0x99, 0xc6, 0x7d, + 0x3a, 0x70, 0x7e, 0x59, 0xf2, 0x6d, 0x67, 0x50, 0xea, 0x7d, 0xe2, 0x25, 0xbe, 0xe5, 0x66, 0xcb, + 0xbe, 0x91, 0xb2, 0xfe, 0x99, 0x03, 0x54, 0xce, 0x1e, 0xc0, 0xf9, 0x83, 0x05, 0xcb, 0x65, 0xab, + 0xf2, 0x90, 0xef, 0x8b, 0x7e, 0x48, 0x5f, 0x64, 0xbd, 0x28, 0xbf, 0xf1, 0x72, 0xf9, 0x3d, 0x81, + 0xb9, 0x21, 0xa7, 0x64, 0x7a, 0x1b, 0x6f, 0x8d, 0x94, 0x63, 0xa5, 0xee, 0xea, 0xce, 0x96, 0xcf, + 0x21, 0x9d, 0xbf, 0x5b, 0xb0, 0x98, 0xf9, 0x98, 0x5f, 0x16, 0xfa, 0x31, 0xa0, 0xfc, 0x78, 0xc5, + 0x66, 0x98, 0xa4, 0xd4, 0x42, 0xc6, 0xc9, 0xd6, 0xc2, 0x22, 0x35, 0xc6, 0x4b, 0xa9, 0x81, 0x3e, + 0x84, 0xa5, 0xdc, 0xe5, 0xc8, 0x04, 0x68, 0xe4, 0x28, 0xe6, 0xbb, 0x6f, 0x4e, 0x42, 0xeb, 0x50, + 0xff, 0x05, 0x67, 0x61, 0xf9, 0xe3, 0x53, 0xc5, 0x05, 0x4d, 0x4a, 0xbe, 0x2b, 0x39, 0xbf, 0xb6, + 0x8a, 0x79, 0x99, 0xce, 0xdc, 0xed, 0x20, 0x48, 0xdf, 0x38, 0x50, 0x04, 0x53, 0xd9, 0xd4, 0x4e, + 0x1a, 0xcc, 0xda, 0xb9, 0x9b, 0xc5, 0x1e, 0x25, 0x66, 0xb9, 0xb8, 0xa9, 0x6b, 0xf0, 0x8b, 0x6f, + 0xd6, 0xaf, 0x75, 0x98, 0xea, 0xf6, 0xdb, 0x4d, 0xc2, 0x7b, 0xe9, 0x17, 0xb9, 0xf4, 0xdf, 0x75, + 0xe9, 0x1f, 0xb7, 0xd4, 0x20, 0xa2, 0x32, 0xd3, 0x91, 0x7f, 0xfa, 0xcf, 0x9f, 0xdf, 0xb4, 0xdc, + 0xcc, 0xcc, 0xce, 0x93, 0x2f, 0x9f, 0x35, 0xac, 0xaf, 0x9e, 0x35, 0xac, 0x7f, 0x3f, 0x6b, 0x58, + 0x9f, 0x3e, 0x6f, 0x8c, 0x7d, 0xf5, 0xbc, 0x31, 0xf6, 0xaf, 0xe7, 0x8d, 0xb1, 0x8f, 0xdf, 0x3d, + 0x0b, 0x5a, 0x04, 0xf1, 0x7a, 0xfe, 0xc1, 0x34, 0x7e, 0xa7, 0xf5, 0xc9, 0xf0, 0xe7, 0x58, 0x63, + 0xaf, 0x3d, 0x69, 0xda, 0xed, 0xdb, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x5f, 0xaa, 0x2b, 0x22, + 0xbf, 0x15, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -2281,6 +2306,11 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.NumberOfEpochsToStartReceivingRewards != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.NumberOfEpochsToStartReceivingRewards)) + i-- + dAtA[i] = 0x58 + } if m.BlocksPerEpoch != 0 { i = encodeVarintProvider(dAtA, i, uint64(m.BlocksPerEpoch)) i-- @@ -3015,6 +3045,11 @@ func (m *ConsumerValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.JoinHeight != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.JoinHeight)) + i-- + dAtA[i] = 0x20 + } if m.ConsumerPublicKey != nil { { size, err := m.ConsumerPublicKey.MarshalToSizedBuffer(dAtA[:i]) @@ -3334,6 +3369,9 @@ func (m *Params) Size() (n int) { if m.BlocksPerEpoch != 0 { n += 1 + sovProvider(uint64(m.BlocksPerEpoch)) } + if m.NumberOfEpochsToStartReceivingRewards != 0 { + n += 1 + sovProvider(uint64(m.NumberOfEpochsToStartReceivingRewards)) + } return n } @@ -3630,6 +3668,9 @@ func (m *ConsumerValidator) Size() (n int) { l = m.ConsumerPublicKey.Size() n += 1 + l + sovProvider(uint64(l)) } + if m.JoinHeight != 0 { + n += 1 + sovProvider(uint64(m.JoinHeight)) + } return n } @@ -5509,6 +5550,25 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumberOfEpochsToStartReceivingRewards", wireType) + } + m.NumberOfEpochsToStartReceivingRewards = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NumberOfEpochsToStartReceivingRewards |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) @@ -7447,6 +7507,25 @@ func (m *ConsumerValidator) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JoinHeight", wireType) + } + m.JoinHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JoinHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:])