From fee04fc792669d2ec328c615adb01242ff4d54bb Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 30 May 2024 15:37:41 +0200 Subject: [PATCH] init commit --- .../ccv/provider/v1/provider.proto | 6 + tests/integration/distribution.go | 108 +++++- x/ccv/provider/keeper/distribution.go | 14 + x/ccv/provider/keeper/distribution_test.go | 5 + x/ccv/provider/keeper/params.go | 9 + x/ccv/provider/keeper/params_test.go | 1 + x/ccv/provider/keeper/proposal_test.go | 3 +- x/ccv/provider/keeper/relay_test.go | 49 +++ x/ccv/provider/keeper/validator_set_update.go | 24 ++ x/ccv/provider/migrations/migrator.go | 8 + .../provider/migrations/v6/migration_test.go | 27 ++ x/ccv/provider/migrations/v6/migrations.go | 18 + x/ccv/provider/module.go | 4 + x/ccv/provider/types/genesis_test.go | 26 +- x/ccv/provider/types/params.go | 55 ++- x/ccv/provider/types/params_test.go | 28 +- x/ccv/provider/types/provider.pb.go | 317 +++++++++++------- 17 files changed, 537 insertions(+), 165 deletions(-) create mode 100644 x/ccv/provider/migrations/v6/migration_test.go create mode 100644 x/ccv/provider/migrations/v6/migrations.go diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 139cc9d25f..856630b7d0 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -212,6 +212,10 @@ 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 during 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 @@ -329,6 +333,8 @@ 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 opted in + int64 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 a71edf7f77..106c1bcd2c 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -934,7 +934,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() @@ -1060,6 +1060,112 @@ 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 `TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights` 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 := sdk.OneDec() + expAllocated := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDecFromIntWithPrec(math.NewInt(999), 2))} + + ctx, _ := s.providerCtx().CacheContext() + // If the provider chain has not reached `GetNumberOfEpochsToStartReceivingRewards * GetBlocksPerEpoch` height yet + // then all validators received rewards. 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 will get rewards + consuVals[0].Height = 1 + consuVals[1].Height = 1 + // last 2 validators were consumer validators since block height 2 and hence will not get rewards because they + // have not been consumer validators for `GetNumberOfEpochsToStartReceivingRewards * GetBlocksPerEpoch` blocks + consuVals[2].Height = 2 + consuVals[3].Height = 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(sdk.NewDec(int64(2))) + + // check that the rewards are allocated to validators + for _, v := range consuVals[0:2] { + valAddr := sdk.ValAddress(v.ProviderConsAddr) + rewards := s.providerApp.GetTestDistributionKeeper().GetValidatorOutstandingRewards( + ctx, + valAddr, + ) + 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))) + } + + for _, v := range consuVals[2:4] { + valAddr := sdk.ValAddress(v.ProviderConsAddr) + rewards := s.providerApp.GetTestDistributionKeeper().GetValidatorOutstandingRewards( + ctx, + valAddr, + ) + 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 67e6193e5d..a0bd0f49e5 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -147,6 +147,13 @@ func (k Keeper) AllocateTokensToConsumerValidators( // Allocate tokens by iterating over the consumer validators for _, consumerVal := range k.GetConsumerValSet(ctx, chainID) { + + // do not distribute rewards to validators that have nto been validating long enough + numberOfBlocksToStartReceivingRewards := k.GetNumberOfEpochsToStartReceivingRewards(ctx) * k.GetBlocksPerEpoch(ctx) + if ctx.BlockHeight() >= numberOfBlocksToStartReceivingRewards && ctx.BlockHeight()-consumerVal.Height < numberOfBlocksToStartReceivingRewards { + continue + } + consAddr := sdk.ConsAddress(consumerVal.ProviderConsAddr) // get the validator tokens fraction using its voting power @@ -240,6 +247,13 @@ func (k Keeper) GetConsumerRewardsPool(ctx sdk.Context) sdk.Coins { func (k Keeper) ComputeConsumerTotalVotingPower(ctx sdk.Context, chainID string) (totalPower int64) { // sum the opted-in validators set voting powers for _, v := range k.GetConsumerValSet(ctx, chainID) { + + // only consider the voting power of validators that would receive rewards (i.e., validator has been validating for a number of blocks) + numberOfBlocksToStartReceivingRewards := k.GetNumberOfEpochsToStartReceivingRewards(ctx) * k.GetBlocksPerEpoch(ctx) + if ctx.BlockHeight() >= numberOfBlocksToStartReceivingRewards && ctx.BlockHeight()-v.Height < numberOfBlocksToStartReceivingRewards { + continue + } + totalPower += v.Power } diff --git a/x/ccv/provider/keeper/distribution_test.go b/x/ccv/provider/keeper/distribution_test.go index d95ae598c0..22f5f226ce 100644 --- a/x/ccv/provider/keeper/distribution_test.go +++ b/x/ccv/provider/keeper/distribution_test.go @@ -23,6 +23,10 @@ func TestComputeConsumerTotalVotingPower(t *testing.T) { keeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + params := providertypes.DefaultParams() + params.BlocksPerEpoch = 1 + keeper.SetParams(ctx, params) + createVal := func(power int64) tmtypes.Validator { signer := tmtypes.NewMockPV() val := tmtypes.NewValidator(signer.PrivKey.PubKey(), power) @@ -48,6 +52,7 @@ func TestComputeConsumerTotalVotingPower(t *testing.T) { types.ConsumerValidator{ ProviderConsAddr: val.Address, Power: val.VotingPower, + Height: -100, }, ) diff --git a/x/ccv/provider/keeper/params.go b/x/ccv/provider/keeper/params.go index f74baf656e..b5854e5b59 100644 --- a/x/ccv/provider/keeper/params.go +++ b/x/ccv/provider/keeper/params.go @@ -85,6 +85,14 @@ func (k Keeper) GetBlocksPerEpoch(ctx sdk.Context) int64 { return b } +// 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 { + var b int64 + k.paramSpace.Get(ctx, types.KeyNumberOfEpochsToStartReceivingRewards, &b) + return b +} + // GetParams returns the paramset for the provider module func (k Keeper) GetParams(ctx sdk.Context) types.Params { return types.NewParams( @@ -97,6 +105,7 @@ func (k Keeper) GetParams(ctx sdk.Context) types.Params { k.GetSlashMeterReplenishFraction(ctx), k.GetConsumerRewardDenomRegistrationFee(ctx), k.GetBlocksPerEpoch(ctx), + k.GetNumberOfEpochsToStartReceivingRewards(ctx), ) } diff --git a/x/ccv/provider/keeper/params_test.go b/x/ccv/provider/keeper/params_test.go index 88175431c0..9dd10076a6 100644 --- a/x/ccv/provider/keeper/params_test.go +++ b/x/ccv/provider/keeper/params_test.go @@ -49,6 +49,7 @@ func TestParams(t *testing.T) { Amount: sdk.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 84cb91acb8..36690ae8dd 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -789,7 +789,8 @@ func TestMakeConsumerGenesis(t *testing.T) { Denom: "stake", Amount: sdk.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 5f74d93424..5d3277c59d 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -87,6 +87,55 @@ func TestQueueVSCPackets(t *testing.T) { } } +// TestQueueVSCPacketsDoesNotResetConsumerValidatorsHeights TODO fixme +// more of a regress test to verify that the heights are updated correctly ... +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) + valAConsAddr, _ := valA.GetConsAddr() + valAPubKey, _ := valA.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valAConsAddr).Return(valA, true).AnyTimes() + valB := createStakingValidator(ctx, mocks, 2, 2) + valBConsAddr, _ := valB.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(valB, true).AnyTimes() + mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{valA, valB}).AnyTimes() + + // 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, + Height: 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.Height, cv.Height, "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.Height, "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 264fdd67f2..0bb33523cb 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -71,6 +71,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, @@ -151,10 +168,17 @@ func (k Keeper) CreateConsumerValidator(ctx sdk.Context, chainID string, validat } } + height := ctx.BlockHeight() + // if validator was a consumer validator, then do not update the height the validator became a consumer validator + if v, found := k.GetConsumerValidator(ctx, chainID, types.ProviderConsAddress{Address: consAddr}); found { + height = v.Height + } + return types.ConsumerValidator{ ProviderConsAddr: consAddr, Power: power, ConsumerPublicKey: &consumerPublicKey, + Height: height, }, nil } diff --git a/x/ccv/provider/migrations/migrator.go b/x/ccv/provider/migrations/migrator.go index c8b464a7a3..f140a210fc 100644 --- a/x/ccv/provider/migrations/migrator.go +++ b/x/ccv/provider/migrations/migrator.go @@ -8,6 +8,7 @@ import ( v3 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v3" v4 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v4" v5 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v5" + v6 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v6" ) // Migrator is a struct for handling in-place store migrations. @@ -47,3 +48,10 @@ func (m Migrator) Migrate4to5(ctx sdktypes.Context) error { v5.MigrateTopNForRegisteredChains(ctx, m.providerKeeper) return nil } + +// Migrate5to6 migrates x/ccvprovider state from consensus version 5 to 6. +// The migration consists of a provider chain param addition. +func (m Migrator) Migrate5to6(ctx sdktypes.Context) error { + v6.MigrateParams(ctx, m.paramSpace) + return nil +} diff --git a/x/ccv/provider/migrations/v6/migration_test.go b/x/ccv/provider/migrations/v6/migration_test.go new file mode 100644 index 0000000000..080b694279 --- /dev/null +++ b/x/ccv/provider/migrations/v6/migration_test.go @@ -0,0 +1,27 @@ +package v4 + +import ( + "testing" + + "github.com/stretchr/testify/require" + + testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" +) + +func TestMigrateParams(t *testing.T) { + inMemParams := testutil.NewInMemKeeperParams(t) + _, ctx, ctrl, _ := testutil.GetProviderKeeperAndCtx(t, inMemParams) + defer ctrl.Finish() + + // initially number of epochs param does not exist + require.False(t, inMemParams.ParamsSubspace.Has(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards)) + + MigrateParams(ctx, *inMemParams.ParamsSubspace) + + // after migration, number of epochs epoch 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 new file mode 100644 index 0000000000..f182609698 --- /dev/null +++ b/x/ccv/provider/migrations/v6/migrations.go @@ -0,0 +1,18 @@ +package v4 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" +) + +// MigrateParams adds missing provider chain params to the param store. +func MigrateParams(ctx sdk.Context, paramsSubspace paramtypes.Subspace) { + if paramsSubspace.HasKeyTable() { + paramsSubspace.Set(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards, providertypes.DefaultNumberOfEpochsToStartReceivingRewards) + } else { + paramsSubspace.WithKeyTable(providertypes.ParamKeyTable()) + paramsSubspace.Set(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards, providertypes.DefaultNumberOfEpochsToStartReceivingRewards) + } +} diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index 6b616f0de8..fc2b0114b1 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -118,6 +118,10 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(providertypes.ModuleName, 4, m.Migrate4to5); err != nil { panic(fmt.Sprintf("failed to register migrator for %s: %s", providertypes.ModuleName, err)) } + if err := cfg.RegisterMigration(providertypes.ModuleName, 5, m.Migrate5to6); 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. diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index 41a716757f..323aaa49be 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -81,7 +81,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: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -102,7 +102,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: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -123,7 +123,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: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -144,7 +144,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: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -171,7 +171,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -198,7 +198,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -225,7 +225,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(1000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(1000000)}, 600, 24), nil, nil, nil, @@ -252,7 +252,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -279,7 +279,7 @@ func TestValidateGenesisState(t *testing.T) { 0, // 0 vsc timeout here types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -306,7 +306,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, 0, // 0 slash meter replenish period here types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -333,7 +333,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, "1.15", - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -685,7 +685,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: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -706,7 +706,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: sdk.NewInt(-1000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-1000000)}, 600, 24), nil, nil, nil, diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index a0a7a5ed7a..f7ecf22d41 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -41,18 +41,30 @@ 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 stat 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 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 @@ -71,17 +83,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, } } @@ -113,6 +127,7 @@ func DefaultParams() Params { Amount: sdk.NewInt(10000000), }, DefaultBlocksPerEpoch, + DefaultNumberOfEpochsToStartReceivingRewards, ) } @@ -145,9 +160,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 received rewards is invalid: %s", err) + } return nil } @@ -163,6 +181,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 4e72c233af..bcec7c7c77 100644 --- a/x/ccv/provider/types/params_test.go +++ b/x/ccv/provider/types/params_test.go @@ -24,39 +24,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: sdk.NewInt(10000000)}, 1000), true}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.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: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.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: sdk.NewInt(10000000)}, 1000), true}, + "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.NewInt(10000000)}, 1000), false}, + "0.33", 0, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, 0, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 0, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, 0, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "1.5", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.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: sdk.NewInt(-10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.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: sdk.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 4f0a2fd605..38cd6cefdf 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -474,6 +474,9 @@ 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 during 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{} } @@ -572,6 +575,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 { @@ -1424,6 +1434,8 @@ 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 opted in + Height int64 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` } func (m *ConsumerValidator) Reset() { *m = ConsumerValidator{} } @@ -1480,6 +1492,13 @@ func (m *ConsumerValidator) GetConsumerPublicKey() *crypto.PublicKey { return nil } +func (m *ConsumerValidator) GetHeight() int64 { + if m != nil { + return m.Height + } + 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. @@ -1559,127 +1578,131 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1919 bytes of a gzipped FileDescriptorProto + // 1977 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4f, 0x6f, 0x1b, 0xc7, - 0x15, 0xd7, 0x8a, 0x94, 0x45, 0x0e, 0xf5, 0x77, 0xa4, 0xc4, 0x2b, 0x55, 0xa5, 0xe8, 0x4d, 0x93, - 0xaa, 0x71, 0xbd, 0x1b, 0x29, 0x2d, 0x60, 0x18, 0x0d, 0x02, 0x89, 0x72, 0x62, 0x59, 0x89, 0xcd, - 0xac, 0x54, 0x19, 0x6d, 0x0f, 0x8b, 0xe1, 0xec, 0x98, 0x1c, 0x68, 0xb9, 0xb3, 0x9e, 0x19, 0xae, - 0xc2, 0x4b, 0xcf, 0x3d, 0xb4, 0x40, 0x7a, 0x0b, 0x7a, 0x69, 0x5a, 0xa0, 0x40, 0xd1, 0x4b, 0xfb, - 0x31, 0x72, 0xcc, 0xb1, 0xa7, 0xa4, 0xb0, 0x0f, 0x3d, 0xf4, 0x4b, 0x14, 0x33, 0xfb, 0x97, 0x94, - 0xe4, 0xd2, 0x48, 0x73, 0x91, 0x76, 0xdf, 0xbc, 0xf7, 0x7b, 0x6f, 0xe6, 0xbd, 0x79, 0xbf, 0xc7, - 0x05, 0x7b, 0x34, 0x94, 0x84, 0xe3, 0x3e, 0xa2, 0xa1, 0x27, 0x08, 0x1e, 0x72, 0x2a, 0x47, 0x0e, - 0xc6, 0xb1, 0x13, 0x71, 0x16, 0x53, 0x9f, 0x70, 0x27, 0xde, 0xcd, 0x9f, 0xed, 0x88, 0x33, 0xc9, - 0xe0, 0x1b, 0x57, 0xd8, 0xd8, 0x18, 0xc7, 0x76, 0xae, 0x17, 0xef, 0x6e, 0xbe, 0x79, 0x1d, 0x70, - 0xbc, 0xeb, 0x5c, 0x50, 0x4e, 0x12, 0xac, 0xcd, 0xf5, 0x1e, 0xeb, 0x31, 0xfd, 0xe8, 0xa8, 0xa7, - 0x54, 0xba, 0xdd, 0x63, 0xac, 0x17, 0x10, 0x47, 0xbf, 0x75, 0x87, 0x4f, 0x1d, 0x49, 0x07, 0x44, - 0x48, 0x34, 0x88, 0x52, 0x85, 0xe6, 0xa4, 0x82, 0x3f, 0xe4, 0x48, 0x52, 0x16, 0x66, 0x00, 0xb4, - 0x8b, 0x1d, 0xcc, 0x38, 0x71, 0x70, 0x40, 0x49, 0x28, 0x95, 0xd7, 0xe4, 0x29, 0x55, 0x70, 0x94, - 0x42, 0x40, 0x7b, 0x7d, 0x99, 0x88, 0x85, 0x23, 0x49, 0xe8, 0x13, 0x3e, 0xa0, 0x89, 0x72, 0xf1, - 0x96, 0x1a, 0x6c, 0x95, 0xd6, 0x31, 0x1f, 0x45, 0x92, 0x39, 0xe7, 0x64, 0x24, 0xd2, 0xd5, 0xb7, - 0x30, 0x13, 0x03, 0x26, 0x1c, 0xa2, 0xf6, 0x1f, 0x62, 0xe2, 0xc4, 0xbb, 0x5d, 0x22, 0xd1, 0x6e, - 0x2e, 0xc8, 0xe2, 0x4e, 0xf5, 0xba, 0x48, 0x14, 0x3a, 0x98, 0xd1, 0x2c, 0xee, 0x55, 0x34, 0xa0, - 0x21, 0x73, 0xf4, 0xdf, 0x44, 0x64, 0xfd, 0xb6, 0x06, 0xcc, 0x36, 0x0b, 0xc5, 0x70, 0x40, 0xf8, - 0xbe, 0xef, 0x53, 0xb5, 0xcb, 0x0e, 0x67, 0x11, 0x13, 0x28, 0x80, 0xeb, 0x60, 0x4e, 0x52, 0x19, - 0x10, 0xd3, 0x68, 0x19, 0x3b, 0x75, 0x37, 0x79, 0x81, 0x2d, 0xd0, 0xf0, 0x89, 0xc0, 0x9c, 0x46, - 0x4a, 0xd9, 0x9c, 0xd5, 0x6b, 0x65, 0x11, 0xdc, 0x00, 0xb5, 0x24, 0x35, 0xd4, 0x37, 0x2b, 0x7a, - 0x79, 0x5e, 0xbf, 0x1f, 0xf9, 0xf0, 0x43, 0xb0, 0x44, 0x43, 0x2a, 0x29, 0x0a, 0xbc, 0x3e, 0x51, - 0x07, 0x64, 0x56, 0x5b, 0xc6, 0x4e, 0x63, 0x6f, 0xd3, 0xa6, 0x5d, 0x6c, 0xab, 0x33, 0xb5, 0xd3, - 0x93, 0x8c, 0x77, 0xed, 0x07, 0x5a, 0xe3, 0xa0, 0xfa, 0xe5, 0xd7, 0xdb, 0x33, 0xee, 0x62, 0x6a, - 0x97, 0x08, 0xe1, 0x2d, 0xb0, 0xd0, 0x23, 0x21, 0x11, 0x54, 0x78, 0x7d, 0x24, 0xfa, 0xe6, 0x5c, - 0xcb, 0xd8, 0x59, 0x70, 0x1b, 0xa9, 0xec, 0x01, 0x12, 0x7d, 0xb8, 0x0d, 0x1a, 0x5d, 0x1a, 0x22, - 0x3e, 0x4a, 0x34, 0x6e, 0x68, 0x0d, 0x90, 0x88, 0xb4, 0x42, 0x1b, 0x00, 0x11, 0xa1, 0x8b, 0xd0, - 0x53, 0x05, 0x60, 0xce, 0xa7, 0x81, 0x24, 0xc9, 0xb7, 0xb3, 0xe4, 0xdb, 0xa7, 0x59, 0x75, 0x1c, - 0xd4, 0x54, 0x20, 0x9f, 0x7d, 0xb3, 0x6d, 0xb8, 0x75, 0x6d, 0xa7, 0x56, 0xe0, 0x23, 0xb0, 0x32, - 0x0c, 0xbb, 0x2c, 0xf4, 0x69, 0xd8, 0xf3, 0x22, 0xc2, 0x29, 0xf3, 0xcd, 0x9a, 0x86, 0xda, 0xb8, - 0x04, 0x75, 0x98, 0xd6, 0x51, 0x82, 0xf4, 0xb9, 0x42, 0x5a, 0xce, 0x8d, 0x3b, 0xda, 0x16, 0x7e, - 0x02, 0x20, 0xc6, 0xb1, 0x0e, 0x89, 0x0d, 0x65, 0x86, 0x58, 0x9f, 0x1e, 0x71, 0x05, 0xe3, 0xf8, - 0x34, 0xb1, 0x4e, 0x21, 0x7f, 0x05, 0x6e, 0x4a, 0x8e, 0x42, 0xf1, 0x94, 0xf0, 0x49, 0x5c, 0x30, - 0x3d, 0xee, 0x6b, 0x19, 0xc6, 0x38, 0xf8, 0x03, 0xd0, 0xc2, 0x69, 0x01, 0x79, 0x9c, 0xf8, 0x54, - 0x48, 0x4e, 0xbb, 0x43, 0x65, 0xeb, 0x3d, 0xe5, 0x08, 0xeb, 0x1a, 0x69, 0xe8, 0x22, 0x68, 0x66, - 0x7a, 0xee, 0x98, 0xda, 0x07, 0xa9, 0x16, 0x7c, 0x0c, 0x7e, 0xd0, 0x0d, 0x18, 0x3e, 0x17, 0x2a, - 0x38, 0x6f, 0x0c, 0x49, 0xbb, 0x1e, 0x50, 0x21, 0x14, 0xda, 0x42, 0xcb, 0xd8, 0xa9, 0xb8, 0xb7, - 0x12, 0xdd, 0x0e, 0xe1, 0x87, 0x25, 0xcd, 0xd3, 0x92, 0x22, 0xbc, 0x03, 0x60, 0x9f, 0x0a, 0xc9, - 0x38, 0xc5, 0x28, 0xf0, 0x48, 0x28, 0x39, 0x25, 0xc2, 0x5c, 0xd4, 0xe6, 0xab, 0xc5, 0xca, 0xfd, - 0x64, 0x01, 0x3e, 0x04, 0xb7, 0xae, 0x75, 0xea, 0xe1, 0x3e, 0x0a, 0x43, 0x12, 0x98, 0x4b, 0x7a, - 0x2b, 0xdb, 0xfe, 0x35, 0x3e, 0xdb, 0x89, 0x1a, 0x5c, 0x03, 0x73, 0x92, 0x45, 0xde, 0x23, 0x73, - 0xb9, 0x65, 0xec, 0x2c, 0xba, 0x55, 0xc9, 0xa2, 0x47, 0xf0, 0x1d, 0xb0, 0x1e, 0xa3, 0x80, 0xfa, - 0x48, 0x32, 0x2e, 0xbc, 0x88, 0x5d, 0x10, 0xee, 0x61, 0x14, 0x99, 0x2b, 0x5a, 0x07, 0x16, 0x6b, - 0x1d, 0xb5, 0xd4, 0x46, 0x11, 0x7c, 0x1b, 0xac, 0xe6, 0x52, 0x4f, 0x10, 0xa9, 0xd5, 0x57, 0xb5, - 0xfa, 0x72, 0xbe, 0x70, 0x42, 0xa4, 0xd2, 0xdd, 0x02, 0x75, 0x14, 0x04, 0xec, 0x22, 0xa0, 0x42, - 0x9a, 0xb0, 0x55, 0xd9, 0xa9, 0xbb, 0x85, 0x00, 0x6e, 0x82, 0x9a, 0x4f, 0xc2, 0x91, 0x5e, 0x5c, - 0xd3, 0x8b, 0xf9, 0xfb, 0xbd, 0xda, 0x6f, 0xbe, 0xd8, 0x9e, 0xf9, 0xfc, 0x8b, 0xed, 0x19, 0xeb, - 0xef, 0x06, 0xb8, 0xd9, 0xce, 0xb3, 0x34, 0x60, 0x31, 0x0a, 0xbe, 0xcb, 0x6e, 0xb0, 0x0f, 0xea, - 0x42, 0x1d, 0x93, 0xbe, 0x7f, 0xd5, 0x57, 0xb8, 0x7f, 0x35, 0x65, 0xa6, 0x16, 0xac, 0x3f, 0x1a, - 0x60, 0xfd, 0xfe, 0xb3, 0x21, 0x8d, 0x19, 0x46, 0xff, 0x97, 0xe6, 0x75, 0x0c, 0x16, 0x49, 0x09, - 0x4f, 0x98, 0x95, 0x56, 0x65, 0xa7, 0xb1, 0xf7, 0xa6, 0x9d, 0x34, 0x57, 0x3b, 0xef, 0xb9, 0x69, - 0x83, 0xb5, 0xcb, 0xde, 0xdd, 0x71, 0xdb, 0x7b, 0xb3, 0xa6, 0x61, 0xfd, 0xd9, 0x00, 0x9b, 0xaa, - 0x2c, 0x7a, 0xc4, 0x25, 0x17, 0x88, 0xfb, 0x87, 0x24, 0x64, 0x03, 0xf1, 0xad, 0xe3, 0xb4, 0xc0, - 0xa2, 0xaf, 0x91, 0x3c, 0xc9, 0x3c, 0xe4, 0xfb, 0x3a, 0x4e, 0xad, 0xa3, 0x84, 0xa7, 0x6c, 0xdf, - 0xf7, 0xe1, 0x0e, 0x58, 0x29, 0x74, 0xb8, 0xca, 0xa7, 0x3a, 0x66, 0xa5, 0xb6, 0x94, 0xa9, 0xe9, - 0x2c, 0x13, 0xeb, 0x3f, 0x06, 0x58, 0xf9, 0x30, 0x60, 0x5d, 0x14, 0x9c, 0x04, 0x48, 0xf4, 0xd5, - 0x95, 0x18, 0xa9, 0xf4, 0x70, 0x92, 0xf6, 0x22, 0x1d, 0xde, 0xd4, 0xe9, 0x51, 0x66, 0xba, 0x3b, - 0xbe, 0x0f, 0x56, 0xf3, 0xee, 0x90, 0x57, 0x81, 0xde, 0xcd, 0xc1, 0xda, 0xf3, 0xaf, 0xb7, 0x97, - 0xb3, 0x62, 0x6b, 0xeb, 0x8a, 0x38, 0x74, 0x97, 0xf1, 0x98, 0xc0, 0x87, 0x4d, 0xd0, 0xa0, 0x5d, - 0xec, 0x09, 0xf2, 0xcc, 0x0b, 0x87, 0x03, 0x5d, 0x40, 0x55, 0xb7, 0x4e, 0xbb, 0xf8, 0x84, 0x3c, - 0x7b, 0x34, 0x1c, 0xc0, 0x77, 0xc1, 0xeb, 0xd9, 0x60, 0xe0, 0xc5, 0x28, 0xf0, 0x94, 0xbd, 0x3a, - 0x0e, 0xae, 0xeb, 0x69, 0xc1, 0x5d, 0xcb, 0x56, 0xcf, 0x50, 0xa0, 0x9c, 0xed, 0xfb, 0x3e, 0xb7, - 0x5e, 0xcc, 0x81, 0x1b, 0x1d, 0xc4, 0xd1, 0x40, 0xc0, 0x53, 0xb0, 0x2c, 0xc9, 0x20, 0x0a, 0x90, - 0x24, 0x5e, 0xc2, 0x3c, 0xe9, 0x4e, 0x6f, 0x6b, 0x46, 0x2a, 0x93, 0xb8, 0x5d, 0xa2, 0xed, 0x78, - 0xd7, 0x6e, 0x6b, 0xe9, 0x89, 0x44, 0x92, 0xb8, 0x4b, 0x19, 0x46, 0x22, 0x84, 0x77, 0x81, 0x29, - 0xf9, 0x50, 0xc8, 0x82, 0x13, 0x8a, 0x66, 0x98, 0xe4, 0xf2, 0xf5, 0x6c, 0x3d, 0x69, 0xa3, 0x79, - 0x13, 0xbc, 0xba, 0xfd, 0x57, 0xbe, 0x4d, 0xfb, 0x3f, 0x01, 0x6b, 0x8a, 0x3b, 0x27, 0x31, 0xab, - 0xd3, 0x63, 0xae, 0x2a, 0xfb, 0x71, 0xd0, 0x4f, 0x00, 0x8c, 0x05, 0x9e, 0xc4, 0x9c, 0x7b, 0x85, - 0x38, 0x63, 0x81, 0xc7, 0x21, 0x7d, 0xb0, 0x25, 0x54, 0xf1, 0x79, 0x03, 0x22, 0x35, 0x99, 0x44, - 0x01, 0x09, 0xa9, 0xe8, 0x67, 0xe0, 0x37, 0xa6, 0x07, 0xdf, 0xd0, 0x40, 0x1f, 0x2b, 0x1c, 0x37, - 0x83, 0x49, 0xbd, 0xb4, 0x41, 0xf3, 0x6a, 0x2f, 0x79, 0x82, 0xe6, 0x75, 0x82, 0xbe, 0x77, 0x05, - 0x44, 0x9e, 0x25, 0x01, 0xde, 0x2a, 0x91, 0x9e, 0xba, 0xd5, 0x9e, 0xbe, 0x50, 0x1e, 0x27, 0x3d, - 0xc5, 0x0c, 0x28, 0xe1, 0x3f, 0x42, 0x72, 0xe2, 0x4e, 0xbb, 0x87, 0x1a, 0xcd, 0xf2, 0xce, 0xd1, - 0x66, 0x34, 0x4c, 0xa7, 0x1b, 0xab, 0xe0, 0xc6, 0xbc, 0x47, 0xb8, 0x25, 0xac, 0x0f, 0x08, 0x51, - 0xb7, 0xb9, 0xc4, 0x8f, 0x24, 0x62, 0xb8, 0xaf, 0xf9, 0xbb, 0xe2, 0x2e, 0xe5, 0x5c, 0x78, 0x5f, - 0x49, 0x1f, 0x56, 0x6b, 0xb5, 0x95, 0xba, 0xf5, 0x23, 0x50, 0xd7, 0x97, 0x79, 0x1f, 0x9f, 0x0b, - 0xcd, 0x0e, 0xbe, 0xcf, 0x89, 0x10, 0x44, 0x98, 0x46, 0xca, 0x0e, 0x99, 0xc0, 0x92, 0x60, 0xe3, - 0xba, 0x29, 0x50, 0xc0, 0x27, 0x60, 0x3e, 0x22, 0x7a, 0x44, 0xd1, 0x86, 0x8d, 0xbd, 0xf7, 0xec, - 0x29, 0x66, 0x74, 0xfb, 0x3a, 0x40, 0x37, 0x43, 0xb3, 0x78, 0x31, 0x7b, 0x4e, 0x90, 0x8d, 0x80, - 0x67, 0x93, 0x4e, 0x7f, 0xf6, 0x4a, 0x4e, 0x27, 0xf0, 0x0a, 0x9f, 0xb7, 0x41, 0x63, 0x3f, 0xd9, - 0xf6, 0x47, 0x8a, 0x16, 0x2f, 0x1d, 0xcb, 0x42, 0xf9, 0x58, 0x1e, 0x82, 0xa5, 0x94, 0xd0, 0x4f, - 0x99, 0x6e, 0x48, 0xf0, 0xfb, 0x00, 0xa4, 0x93, 0x80, 0x6a, 0x64, 0x49, 0xcb, 0xae, 0xa7, 0x92, - 0x23, 0x7f, 0x8c, 0xeb, 0x66, 0xc7, 0xb8, 0xce, 0x72, 0xc1, 0xf2, 0x99, 0xc0, 0x3f, 0xcf, 0xa6, - 0xbd, 0xc7, 0x91, 0x80, 0xaf, 0x81, 0x1b, 0xea, 0x0e, 0xa5, 0x40, 0x55, 0x77, 0x2e, 0x16, 0xf8, - 0x48, 0x77, 0xed, 0x62, 0xa2, 0x64, 0x91, 0x47, 0x7d, 0x61, 0xce, 0xb6, 0x2a, 0x3b, 0x55, 0x77, - 0x69, 0x58, 0x98, 0x1f, 0xf9, 0xc2, 0xfa, 0x05, 0x68, 0x94, 0x00, 0xe1, 0x12, 0x98, 0xcd, 0xb1, - 0x66, 0xa9, 0x0f, 0xef, 0x81, 0x8d, 0x02, 0x68, 0xbc, 0x0d, 0x27, 0x88, 0x75, 0xf7, 0x66, 0xae, - 0x30, 0xd6, 0x89, 0x85, 0xf5, 0x18, 0xac, 0x1f, 0x15, 0x97, 0x3e, 0x6f, 0xf2, 0x63, 0x3b, 0x34, - 0xc6, 0xd9, 0x7c, 0x0b, 0xd4, 0xf3, 0x5f, 0x52, 0x7a, 0xf7, 0x55, 0xb7, 0x10, 0x58, 0x03, 0xb0, - 0x72, 0x26, 0xf0, 0x09, 0x09, 0xfd, 0x02, 0xec, 0x9a, 0x03, 0x38, 0x98, 0x04, 0x9a, 0x7a, 0x2c, - 0x2f, 0xdc, 0x31, 0xb0, 0x71, 0x56, 0x1e, 0x90, 0x34, 0x01, 0x77, 0x10, 0x3e, 0x27, 0x52, 0x40, - 0x17, 0x54, 0xf5, 0x20, 0x94, 0x54, 0xd6, 0xdd, 0x6b, 0x2b, 0x2b, 0xde, 0xb5, 0xaf, 0x03, 0x39, - 0x44, 0x12, 0xa5, 0x77, 0x57, 0x63, 0x59, 0x3f, 0x04, 0x6b, 0x1f, 0x23, 0x39, 0xe4, 0xc4, 0x1f, - 0xcb, 0xf1, 0x0a, 0xa8, 0xa8, 0xfc, 0x19, 0x3a, 0x7f, 0xea, 0x51, 0xcd, 0x03, 0xe6, 0xfd, 0x4f, - 0x23, 0xc6, 0x25, 0xf1, 0x2f, 0x9d, 0xc8, 0x4b, 0x8e, 0xf7, 0x1c, 0xac, 0xa9, 0xc3, 0x12, 0x24, - 0xf4, 0xbd, 0x7c, 0x9f, 0x49, 0x1e, 0x1b, 0x7b, 0x3f, 0x9d, 0xea, 0x76, 0x4c, 0xba, 0x4b, 0x37, - 0xb0, 0x1a, 0x4f, 0xc8, 0x85, 0xf5, 0x7b, 0x03, 0x98, 0xc7, 0x64, 0xb4, 0x2f, 0x04, 0xed, 0x85, - 0x03, 0x12, 0x4a, 0xd5, 0x03, 0x11, 0x26, 0xea, 0x11, 0xbe, 0x01, 0x16, 0x73, 0xce, 0xd5, 0x54, - 0x6b, 0x68, 0xaa, 0x5d, 0xc8, 0x84, 0xea, 0x82, 0xc1, 0x7b, 0x00, 0x44, 0x9c, 0xc4, 0x1e, 0xf6, - 0xce, 0xc9, 0x28, 0xcd, 0xe2, 0x56, 0x99, 0x42, 0x93, 0xdf, 0xb9, 0x76, 0x67, 0xd8, 0x0d, 0x28, - 0x3e, 0x26, 0x23, 0xb7, 0xa6, 0xf4, 0xdb, 0xc7, 0x64, 0xa4, 0x66, 0x22, 0x3d, 0x1d, 0x6b, 0xde, - 0xab, 0xb8, 0xc9, 0x8b, 0xf5, 0x07, 0x03, 0xdc, 0xcc, 0xd3, 0x91, 0x95, 0x6b, 0x67, 0xd8, 0x55, - 0x16, 0x2f, 0x39, 0xb7, 0x4b, 0xd1, 0xce, 0x5e, 0x11, 0xed, 0xfb, 0x60, 0x21, 0xbf, 0x20, 0x2a, - 0xde, 0xca, 0x14, 0xf1, 0x36, 0x32, 0x8b, 0x63, 0x32, 0xb2, 0x7e, 0x5d, 0x8a, 0xed, 0x60, 0x54, - 0xea, 0x7d, 0xfc, 0x7f, 0xc4, 0x96, 0xbb, 0x2d, 0xc7, 0x86, 0xcb, 0xf6, 0x97, 0x36, 0x50, 0xb9, - 0xbc, 0x01, 0xeb, 0x4f, 0x06, 0x58, 0x2f, 0x7b, 0x15, 0xa7, 0xac, 0xc3, 0x87, 0x21, 0x79, 0x99, - 0xf7, 0xe2, 0xfa, 0xcd, 0x96, 0xaf, 0xdf, 0x13, 0xb0, 0x34, 0x16, 0x94, 0x48, 0x4f, 0xe3, 0x9d, - 0xa9, 0x6a, 0xac, 0xd4, 0x5d, 0xdd, 0xc5, 0xf2, 0x3e, 0x84, 0xf5, 0x17, 0x03, 0xac, 0x66, 0x31, - 0xe6, 0x87, 0x05, 0x7f, 0x0c, 0x60, 0xbe, 0xbd, 0x62, 0x7a, 0x4b, 0x4a, 0x6a, 0x25, 0x5b, 0xc9, - 0x46, 0xb7, 0xa2, 0x34, 0x66, 0x4b, 0xa5, 0x01, 0x3f, 0x02, 0x6b, 0x79, 0xc8, 0x91, 0x4e, 0xd0, - 0xd4, 0x59, 0xcc, 0xe7, 0xd3, 0x5c, 0x64, 0xfd, 0xce, 0x28, 0xe8, 0x30, 0xe1, 0x63, 0xb1, 0x1f, - 0x04, 0xe9, 0x50, 0x0f, 0x23, 0x30, 0x9f, 0x50, 0xbe, 0x48, 0xfb, 0xc7, 0xd6, 0x95, 0xe4, 0x7e, - 0x48, 0xb0, 0xe6, 0xf7, 0xbb, 0xea, 0x8a, 0xfd, 0xed, 0x9b, 0xed, 0xdb, 0x3d, 0x2a, 0xfb, 0xc3, - 0xae, 0x8d, 0xd9, 0xc0, 0x49, 0xbf, 0xd3, 0x24, 0xff, 0xee, 0x08, 0xff, 0xdc, 0x91, 0xa3, 0x88, - 0x88, 0xcc, 0x46, 0xfc, 0xf5, 0xdf, 0xff, 0x78, 0xdb, 0x70, 0x33, 0x37, 0x07, 0x4f, 0xbe, 0x7c, - 0xde, 0x34, 0xbe, 0x7a, 0xde, 0x34, 0xfe, 0xf5, 0xbc, 0x69, 0x7c, 0xf6, 0xa2, 0x39, 0xf3, 0xd5, - 0x8b, 0xe6, 0xcc, 0x3f, 0x5f, 0x34, 0x67, 0x7e, 0xf9, 0xde, 0x65, 0xd0, 0x22, 0x47, 0x77, 0xf2, - 0x2f, 0x63, 0xf1, 0x4f, 0x9c, 0x4f, 0xc7, 0xbf, 0xbb, 0x69, 0x7f, 0xdd, 0x1b, 0xba, 0x9b, 0xbe, - 0xfb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xb7, 0x45, 0x0f, 0xa8, 0x13, 0x00, 0x00, + 0x15, 0xd7, 0x8a, 0x94, 0x44, 0x0e, 0xf5, 0x77, 0xa4, 0xd8, 0x2b, 0x55, 0xa5, 0xe8, 0x4d, 0x9d, + 0xaa, 0x71, 0xbd, 0x8c, 0x94, 0x16, 0x30, 0x8c, 0x06, 0x81, 0x44, 0x39, 0xb1, 0xac, 0xc4, 0x66, + 0x56, 0xaa, 0x8c, 0xa6, 0x87, 0xc5, 0x70, 0x76, 0x44, 0x0e, 0xb4, 0xdc, 0x59, 0xcf, 0x0c, 0x57, + 0xe1, 0xa5, 0xe7, 0x1e, 0x5a, 0x20, 0xbd, 0x05, 0xbd, 0x34, 0xed, 0xa9, 0xe8, 0xa5, 0xfd, 0x02, + 0x45, 0xaf, 0x39, 0xe6, 0xd8, 0x53, 0x52, 0xd8, 0x87, 0x1e, 0xfa, 0x25, 0x8a, 0x99, 0xfd, 0x4b, + 0x4a, 0x72, 0x69, 0xb8, 0xb9, 0x48, 0xbb, 0x6f, 0xde, 0xfb, 0xbd, 0x37, 0xf3, 0xde, 0xbc, 0xdf, + 0xe3, 0x82, 0x5d, 0x1a, 0x48, 0xc2, 0x71, 0x0f, 0xd1, 0xc0, 0x15, 0x04, 0x0f, 0x38, 0x95, 0xc3, + 0x26, 0xc6, 0x51, 0x33, 0xe4, 0x2c, 0xa2, 0x1e, 0xe1, 0xcd, 0x68, 0x27, 0x7b, 0xb6, 0x43, 0xce, + 0x24, 0x83, 0x6f, 0x5e, 0x61, 0x63, 0x63, 0x1c, 0xd9, 0x99, 0x5e, 0xb4, 0xb3, 0x71, 0xfb, 0x3a, + 0xe0, 0x68, 0xa7, 0x79, 0x41, 0x39, 0x89, 0xb1, 0x36, 0xd6, 0xba, 0xac, 0xcb, 0xf4, 0x63, 0x53, + 0x3d, 0x25, 0xd2, 0xad, 0x2e, 0x63, 0x5d, 0x9f, 0x34, 0xf5, 0x5b, 0x67, 0x70, 0xd6, 0x94, 0xb4, + 0x4f, 0x84, 0x44, 0xfd, 0x30, 0x51, 0xa8, 0x8f, 0x2b, 0x78, 0x03, 0x8e, 0x24, 0x65, 0x41, 0x0a, + 0x40, 0x3b, 0xb8, 0x89, 0x19, 0x27, 0x4d, 0xec, 0x53, 0x12, 0x48, 0xe5, 0x35, 0x7e, 0x4a, 0x14, + 0x9a, 0x4a, 0xc1, 0xa7, 0xdd, 0x9e, 0x8c, 0xc5, 0xa2, 0x29, 0x49, 0xe0, 0x11, 0xde, 0xa7, 0xb1, + 0x72, 0xfe, 0x96, 0x18, 0x6c, 0x16, 0xd6, 0x31, 0x1f, 0x86, 0x92, 0x35, 0xcf, 0xc9, 0x50, 0x24, + 0xab, 0x6f, 0x61, 0x26, 0xfa, 0x4c, 0x34, 0x89, 0xda, 0x7f, 0x80, 0x49, 0x33, 0xda, 0xe9, 0x10, + 0x89, 0x76, 0x32, 0x41, 0x1a, 0x77, 0xa2, 0xd7, 0x41, 0x22, 0xd7, 0xc1, 0x8c, 0xa6, 0x71, 0xaf, + 0xa0, 0x3e, 0x0d, 0x58, 0x53, 0xff, 0x8d, 0x45, 0xd6, 0x6f, 0x2a, 0xc0, 0x6c, 0xb1, 0x40, 0x0c, + 0xfa, 0x84, 0xef, 0x79, 0x1e, 0x55, 0xbb, 0x6c, 0x73, 0x16, 0x32, 0x81, 0x7c, 0xb8, 0x06, 0x66, + 0x24, 0x95, 0x3e, 0x31, 0x8d, 0x86, 0xb1, 0x5d, 0x75, 0xe2, 0x17, 0xd8, 0x00, 0x35, 0x8f, 0x08, + 0xcc, 0x69, 0xa8, 0x94, 0xcd, 0x69, 0xbd, 0x56, 0x14, 0xc1, 0x75, 0x50, 0x89, 0x53, 0x43, 0x3d, + 0xb3, 0xa4, 0x97, 0xe7, 0xf4, 0xfb, 0xa1, 0x07, 0x3f, 0x04, 0x8b, 0x34, 0xa0, 0x92, 0x22, 0xdf, + 0xed, 0x11, 0x75, 0x40, 0x66, 0xb9, 0x61, 0x6c, 0xd7, 0x76, 0x37, 0x6c, 0xda, 0xc1, 0xb6, 0x3a, + 0x53, 0x3b, 0x39, 0xc9, 0x68, 0xc7, 0x7e, 0xa8, 0x35, 0xf6, 0xcb, 0x5f, 0x7d, 0xb3, 0x35, 0xe5, + 0x2c, 0x24, 0x76, 0xb1, 0x10, 0xde, 0x02, 0xf3, 0x5d, 0x12, 0x10, 0x41, 0x85, 0xdb, 0x43, 0xa2, + 0x67, 0xce, 0x34, 0x8c, 0xed, 0x79, 0xa7, 0x96, 0xc8, 0x1e, 0x22, 0xd1, 0x83, 0x5b, 0xa0, 0xd6, + 0xa1, 0x01, 0xe2, 0xc3, 0x58, 0x63, 0x56, 0x6b, 0x80, 0x58, 0xa4, 0x15, 0x5a, 0x00, 0x88, 0x10, + 0x5d, 0x04, 0xae, 0x2a, 0x00, 0x73, 0x2e, 0x09, 0x24, 0x4e, 0xbe, 0x9d, 0x26, 0xdf, 0x3e, 0x49, + 0xab, 0x63, 0xbf, 0xa2, 0x02, 0xf9, 0xfc, 0xdb, 0x2d, 0xc3, 0xa9, 0x6a, 0x3b, 0xb5, 0x02, 0x1f, + 0x83, 0xe5, 0x41, 0xd0, 0x61, 0x81, 0x47, 0x83, 0xae, 0x1b, 0x12, 0x4e, 0x99, 0x67, 0x56, 0x34, + 0xd4, 0xfa, 0x25, 0xa8, 0x83, 0xa4, 0x8e, 0x62, 0xa4, 0x2f, 0x14, 0xd2, 0x52, 0x66, 0xdc, 0xd6, + 0xb6, 0xf0, 0x13, 0x00, 0x31, 0x8e, 0x74, 0x48, 0x6c, 0x20, 0x53, 0xc4, 0xea, 0xe4, 0x88, 0xcb, + 0x18, 0x47, 0x27, 0xb1, 0x75, 0x02, 0xf9, 0x4b, 0x70, 0x53, 0x72, 0x14, 0x88, 0x33, 0xc2, 0xc7, + 0x71, 0xc1, 0xe4, 0xb8, 0x6f, 0xa4, 0x18, 0xa3, 0xe0, 0x0f, 0x41, 0x03, 0x27, 0x05, 0xe4, 0x72, + 0xe2, 0x51, 0x21, 0x39, 0xed, 0x0c, 0x94, 0xad, 0x7b, 0xc6, 0x11, 0xd6, 0x35, 0x52, 0xd3, 0x45, + 0x50, 0x4f, 0xf5, 0x9c, 0x11, 0xb5, 0x0f, 0x12, 0x2d, 0xf8, 0x04, 0xfc, 0xa0, 0xe3, 0x33, 0x7c, + 0x2e, 0x54, 0x70, 0xee, 0x08, 0x92, 0x76, 0xdd, 0xa7, 0x42, 0x28, 0xb4, 0xf9, 0x86, 0xb1, 0x5d, + 0x72, 0x6e, 0xc5, 0xba, 0x6d, 0xc2, 0x0f, 0x0a, 0x9a, 0x27, 0x05, 0x45, 0x78, 0x17, 0xc0, 0x1e, + 0x15, 0x92, 0x71, 0x8a, 0x91, 0xef, 0x92, 0x40, 0x72, 0x4a, 0x84, 0xb9, 0xa0, 0xcd, 0x57, 0xf2, + 0x95, 0x07, 0xf1, 0x02, 0x7c, 0x04, 0x6e, 0x5d, 0xeb, 0xd4, 0xc5, 0x3d, 0x14, 0x04, 0xc4, 0x37, + 0x17, 0xf5, 0x56, 0xb6, 0xbc, 0x6b, 0x7c, 0xb6, 0x62, 0x35, 0xb8, 0x0a, 0x66, 0x24, 0x0b, 0xdd, + 0xc7, 0xe6, 0x52, 0xc3, 0xd8, 0x5e, 0x70, 0xca, 0x92, 0x85, 0x8f, 0xe1, 0x3b, 0x60, 0x2d, 0x42, + 0x3e, 0xf5, 0x90, 0x64, 0x5c, 0xb8, 0x21, 0xbb, 0x20, 0xdc, 0xc5, 0x28, 0x34, 0x97, 0xb5, 0x0e, + 0xcc, 0xd7, 0xda, 0x6a, 0xa9, 0x85, 0x42, 0xf8, 0x36, 0x58, 0xc9, 0xa4, 0xae, 0x20, 0x52, 0xab, + 0xaf, 0x68, 0xf5, 0xa5, 0x6c, 0xe1, 0x98, 0x48, 0xa5, 0xbb, 0x09, 0xaa, 0xc8, 0xf7, 0xd9, 0x85, + 0x4f, 0x85, 0x34, 0x61, 0xa3, 0xb4, 0x5d, 0x75, 0x72, 0x01, 0xdc, 0x00, 0x15, 0x8f, 0x04, 0x43, + 0xbd, 0xb8, 0xaa, 0x17, 0xb3, 0xf7, 0xfb, 0x95, 0x5f, 0x7f, 0xb9, 0x35, 0xf5, 0xc5, 0x97, 0x5b, + 0x53, 0xd6, 0x5f, 0x0d, 0x70, 0xb3, 0x95, 0x65, 0xa9, 0xcf, 0x22, 0xe4, 0x7f, 0x97, 0xdd, 0x60, + 0x0f, 0x54, 0x85, 0x3a, 0x26, 0x7d, 0xff, 0xca, 0xaf, 0x70, 0xff, 0x2a, 0xca, 0x4c, 0x2d, 0x58, + 0x7f, 0x30, 0xc0, 0xda, 0x83, 0x67, 0x03, 0x1a, 0x31, 0x8c, 0xfe, 0x2f, 0xcd, 0xeb, 0x08, 0x2c, + 0x90, 0x02, 0x9e, 0x30, 0x4b, 0x8d, 0xd2, 0x76, 0x6d, 0xf7, 0xb6, 0x1d, 0x37, 0x57, 0x3b, 0xeb, + 0xb9, 0x49, 0x83, 0xb5, 0x8b, 0xde, 0x9d, 0x51, 0xdb, 0xfb, 0xd3, 0xa6, 0x61, 0xfd, 0xc9, 0x00, + 0x1b, 0xaa, 0x2c, 0xba, 0xc4, 0x21, 0x17, 0x88, 0x7b, 0x07, 0x24, 0x60, 0x7d, 0xf1, 0xda, 0x71, + 0x5a, 0x60, 0xc1, 0xd3, 0x48, 0xae, 0x64, 0x2e, 0xf2, 0x3c, 0x1d, 0xa7, 0xd6, 0x51, 0xc2, 0x13, + 0xb6, 0xe7, 0x79, 0x70, 0x1b, 0x2c, 0xe7, 0x3a, 0x5c, 0xe5, 0x53, 0x1d, 0xb3, 0x52, 0x5b, 0x4c, + 0xd5, 0x74, 0x96, 0x89, 0xf5, 0x1f, 0x03, 0x2c, 0x7f, 0xe8, 0xb3, 0x0e, 0xf2, 0x8f, 0x7d, 0x24, + 0x7a, 0xea, 0x4a, 0x0c, 0x55, 0x7a, 0x38, 0x49, 0x7a, 0x91, 0x0e, 0x6f, 0xe2, 0xf4, 0x28, 0x33, + 0xdd, 0x1d, 0xdf, 0x07, 0x2b, 0x59, 0x77, 0xc8, 0xaa, 0x40, 0xef, 0x66, 0x7f, 0xf5, 0xf9, 0x37, + 0x5b, 0x4b, 0x69, 0xb1, 0xb5, 0x74, 0x45, 0x1c, 0x38, 0x4b, 0x78, 0x44, 0xe0, 0xc1, 0x3a, 0xa8, + 0xd1, 0x0e, 0x76, 0x05, 0x79, 0xe6, 0x06, 0x83, 0xbe, 0x2e, 0xa0, 0xb2, 0x53, 0xa5, 0x1d, 0x7c, + 0x4c, 0x9e, 0x3d, 0x1e, 0xf4, 0xe1, 0xbb, 0xe0, 0x46, 0x3a, 0x18, 0xb8, 0x11, 0xf2, 0x5d, 0x65, + 0xaf, 0x8e, 0x83, 0xeb, 0x7a, 0x9a, 0x77, 0x56, 0xd3, 0xd5, 0x53, 0xe4, 0x2b, 0x67, 0x7b, 0x9e, + 0xc7, 0xad, 0x7f, 0xcc, 0x82, 0xd9, 0x36, 0xe2, 0xa8, 0x2f, 0xe0, 0x09, 0x58, 0x92, 0xa4, 0x1f, + 0xfa, 0x48, 0x12, 0x37, 0x66, 0x9e, 0x64, 0xa7, 0x77, 0x34, 0x23, 0x15, 0x49, 0xdc, 0x2e, 0xd0, + 0x76, 0xb4, 0x63, 0xb7, 0xb4, 0xf4, 0x58, 0x22, 0x49, 0x9c, 0xc5, 0x14, 0x23, 0x16, 0xc2, 0x7b, + 0xc0, 0x94, 0x7c, 0x20, 0x64, 0xce, 0x09, 0x79, 0x33, 0x8c, 0x73, 0x79, 0x23, 0x5d, 0x8f, 0xdb, + 0x68, 0xd6, 0x04, 0xaf, 0x6e, 0xff, 0xa5, 0xd7, 0x69, 0xff, 0xc7, 0x60, 0x55, 0x71, 0xe7, 0x38, + 0x66, 0x79, 0x72, 0xcc, 0x15, 0x65, 0x3f, 0x0a, 0xfa, 0x09, 0x80, 0x91, 0xc0, 0xe3, 0x98, 0x33, + 0xaf, 0x10, 0x67, 0x24, 0xf0, 0x28, 0xa4, 0x07, 0x36, 0x85, 0x2a, 0x3e, 0xb7, 0x4f, 0xa4, 0x26, + 0x93, 0xd0, 0x27, 0x01, 0x15, 0xbd, 0x14, 0x7c, 0x76, 0x72, 0xf0, 0x75, 0x0d, 0xf4, 0xb1, 0xc2, + 0x71, 0x52, 0x98, 0xc4, 0x4b, 0x0b, 0xd4, 0xaf, 0xf6, 0x92, 0x25, 0x68, 0x4e, 0x27, 0xe8, 0x7b, + 0x57, 0x40, 0x64, 0x59, 0x12, 0xe0, 0xad, 0x02, 0xe9, 0xa9, 0x5b, 0xed, 0xea, 0x0b, 0xe5, 0x72, + 0xd2, 0x55, 0xcc, 0x80, 0x62, 0xfe, 0x23, 0x24, 0x23, 0xee, 0xa4, 0x7b, 0xa8, 0xd1, 0x2c, 0xeb, + 0x1c, 0x2d, 0x46, 0x83, 0x64, 0xba, 0xb1, 0x72, 0x6e, 0xcc, 0x7a, 0x84, 0x53, 0xc0, 0xfa, 0x80, + 0x10, 0x75, 0x9b, 0x0b, 0xfc, 0x48, 0x42, 0x86, 0x7b, 0x9a, 0xbf, 0x4b, 0xce, 0x62, 0xc6, 0x85, + 0x0f, 0x94, 0x14, 0x7e, 0x0a, 0xee, 0x04, 0x83, 0x7e, 0x87, 0x70, 0x97, 0x9d, 0xc5, 0x8a, 0xba, + 0x03, 0x08, 0x89, 0xb8, 0x74, 0x39, 0xc1, 0x84, 0x46, 0xaa, 0x32, 0xe3, 0xc8, 0x85, 0xa6, 0xe7, + 0x92, 0x73, 0x3b, 0x36, 0x79, 0x72, 0xa6, 0x31, 0xc4, 0x09, 0x3b, 0x56, 0xea, 0x4e, 0xaa, 0x1d, + 0x07, 0x26, 0x1e, 0x95, 0x2b, 0x95, 0xe5, 0xaa, 0xf5, 0x23, 0x50, 0xd5, 0x8d, 0x62, 0x0f, 0x9f, + 0x0b, 0xcd, 0x3c, 0x9e, 0xc7, 0x89, 0x10, 0x44, 0x98, 0x46, 0xc2, 0x3c, 0xa9, 0xc0, 0x92, 0x60, + 0xfd, 0xba, 0x09, 0x53, 0xc0, 0xa7, 0x60, 0x2e, 0x24, 0x7a, 0xfc, 0xd1, 0x86, 0xb5, 0xdd, 0xf7, + 0xec, 0x09, 0xe6, 0x7f, 0xfb, 0x3a, 0x40, 0x27, 0x45, 0xb3, 0x78, 0x3e, 0xd7, 0x8e, 0x11, 0x99, + 0x80, 0xa7, 0xe3, 0x4e, 0x7f, 0xf6, 0x4a, 0x4e, 0xc7, 0xf0, 0x72, 0x9f, 0x77, 0x40, 0x6d, 0x2f, + 0xde, 0xf6, 0x47, 0x8a, 0x72, 0x2f, 0x1d, 0xcb, 0x7c, 0xf1, 0x58, 0x1e, 0x81, 0xc5, 0x64, 0x58, + 0x38, 0x61, 0xba, 0xd9, 0xc1, 0xef, 0x03, 0x90, 0x4c, 0x19, 0xaa, 0x49, 0xc6, 0x74, 0x50, 0x4d, + 0x24, 0x87, 0xde, 0x08, 0x8f, 0x4e, 0x8f, 0xf0, 0xa8, 0xe5, 0x80, 0xa5, 0x53, 0x81, 0x7f, 0x9e, + 0x4e, 0x92, 0x4f, 0x42, 0x01, 0xdf, 0x00, 0xb3, 0xea, 0x7e, 0x26, 0x40, 0x65, 0x67, 0x26, 0x12, + 0xf8, 0x50, 0x33, 0x42, 0x3e, 0xad, 0xb2, 0xd0, 0xa5, 0x9e, 0x30, 0xa7, 0x1b, 0xa5, 0xed, 0xb2, + 0xb3, 0x38, 0xc8, 0xcd, 0x0f, 0x3d, 0x61, 0xfd, 0x02, 0xd4, 0x0a, 0x80, 0x70, 0x11, 0x4c, 0x67, + 0x58, 0xd3, 0xd4, 0x83, 0xf7, 0xc1, 0x7a, 0x0e, 0x34, 0xda, 0xe2, 0x63, 0xc4, 0xaa, 0x73, 0x33, + 0x53, 0x18, 0xe9, 0xf2, 0xc2, 0x7a, 0x02, 0xd6, 0x0e, 0xf3, 0x86, 0x92, 0x11, 0xc8, 0xc8, 0x0e, + 0x8d, 0xd1, 0x49, 0x61, 0x13, 0x54, 0xb3, 0x5f, 0x69, 0x7a, 0xf7, 0x65, 0x27, 0x17, 0x58, 0x7d, + 0xb0, 0x7c, 0x2a, 0xf0, 0x31, 0x09, 0xbc, 0x1c, 0xec, 0x9a, 0x03, 0xd8, 0x1f, 0x07, 0x9a, 0x78, + 0xe4, 0xcf, 0xdd, 0x31, 0xb0, 0x7e, 0x5a, 0x1c, 0xbe, 0x34, 0xb9, 0xb7, 0x11, 0x3e, 0x27, 0x52, + 0x40, 0x07, 0x94, 0xf5, 0x90, 0x15, 0x57, 0xd6, 0xbd, 0x6b, 0x2b, 0x2b, 0xda, 0xb1, 0xaf, 0x03, + 0x39, 0x40, 0x12, 0x25, 0x7d, 0x41, 0x63, 0x59, 0x3f, 0x04, 0xab, 0x1f, 0x23, 0x39, 0xe0, 0xc4, + 0x1b, 0xc9, 0xf1, 0x32, 0x28, 0xa9, 0xfc, 0x19, 0x3a, 0x7f, 0xea, 0x51, 0xcd, 0x1a, 0xe6, 0x83, + 0xcf, 0x42, 0xc6, 0x25, 0xf1, 0x2e, 0x9d, 0xc8, 0x4b, 0x8e, 0xf7, 0x1c, 0xac, 0xaa, 0xc3, 0x12, + 0x24, 0xf0, 0xdc, 0x6c, 0x9f, 0x71, 0x1e, 0x6b, 0xbb, 0x3f, 0x9d, 0xe8, 0x76, 0x8c, 0xbb, 0x4b, + 0x36, 0xb0, 0x12, 0x8d, 0xc9, 0x85, 0xf5, 0x3b, 0x03, 0x98, 0x47, 0x64, 0xb8, 0x27, 0x04, 0xed, + 0x06, 0x7d, 0x12, 0x48, 0xd5, 0x5f, 0x11, 0x26, 0xea, 0x11, 0xbe, 0x09, 0x16, 0x32, 0x3e, 0xd7, + 0x34, 0x6e, 0x68, 0x1a, 0x9f, 0x4f, 0x85, 0xea, 0x82, 0xc1, 0xfb, 0x00, 0x84, 0x9c, 0x44, 0x2e, + 0x76, 0xcf, 0xc9, 0x30, 0xc9, 0xe2, 0x66, 0x91, 0x9e, 0xe3, 0xdf, 0xd0, 0x76, 0x7b, 0xd0, 0xf1, + 0x29, 0x3e, 0x22, 0x43, 0xa7, 0xa2, 0xf4, 0x5b, 0x47, 0x64, 0xa8, 0xe6, 0x2d, 0x3d, 0x79, 0x6b, + 0x4e, 0x2d, 0x39, 0xf1, 0x8b, 0xf5, 0x7b, 0x03, 0xdc, 0xcc, 0xd2, 0x91, 0x96, 0x6b, 0x7b, 0xd0, + 0x51, 0x16, 0x2f, 0x39, 0xb7, 0x4b, 0xd1, 0x4e, 0x5f, 0x11, 0xed, 0xfb, 0x60, 0x3e, 0xbb, 0x20, + 0x2a, 0xde, 0xd2, 0x04, 0xf1, 0xd6, 0x52, 0x8b, 0x23, 0x32, 0xb4, 0x7e, 0x55, 0x88, 0x6d, 0x7f, + 0x58, 0xe8, 0x7d, 0xfc, 0x7f, 0xc4, 0x96, 0xb9, 0x2d, 0xc6, 0x86, 0x8b, 0xf6, 0x97, 0x36, 0x50, + 0xba, 0xbc, 0x01, 0xeb, 0x8f, 0x06, 0x58, 0x2b, 0x7a, 0x15, 0x27, 0xac, 0xcd, 0x07, 0x01, 0x79, + 0x99, 0xf7, 0xfc, 0xfa, 0x4d, 0x17, 0xaf, 0xdf, 0x53, 0xb0, 0x38, 0x12, 0x94, 0x48, 0x4e, 0xe3, + 0x9d, 0x89, 0x6a, 0xac, 0xd0, 0x5d, 0x9d, 0x85, 0xe2, 0x3e, 0x84, 0xf5, 0x77, 0x03, 0xac, 0xa4, + 0x31, 0x66, 0x87, 0x05, 0x7f, 0x0c, 0x60, 0xb6, 0xbd, 0x7c, 0x32, 0x8c, 0x4b, 0x6a, 0x39, 0x5d, + 0x49, 0xc7, 0xc2, 0xbc, 0x34, 0xa6, 0x0b, 0xa5, 0x01, 0x3f, 0x02, 0xab, 0x59, 0xc8, 0xa1, 0x4e, + 0xd0, 0xc4, 0x59, 0xcc, 0x66, 0xdf, 0x4c, 0x04, 0x6f, 0x80, 0xd9, 0xc2, 0x87, 0x8f, 0x92, 0x93, + 0xbc, 0x59, 0xbf, 0x35, 0x72, 0x9a, 0x4c, 0xa8, 0x76, 0xcf, 0xf7, 0x93, 0x1f, 0x12, 0x30, 0x04, + 0x73, 0x29, 0x59, 0xc7, 0x7d, 0x65, 0xf3, 0xca, 0x81, 0xe2, 0x80, 0x60, 0x3d, 0x53, 0xdc, 0x53, + 0x57, 0xef, 0x2f, 0xdf, 0x6e, 0xdd, 0xe9, 0x52, 0xd9, 0x1b, 0x74, 0x6c, 0xcc, 0xfa, 0xcd, 0xe4, + 0xdb, 0x50, 0xfc, 0xef, 0xae, 0xf0, 0xce, 0x9b, 0x72, 0x18, 0x12, 0x91, 0xda, 0x88, 0x3f, 0xff, + 0xfb, 0x6f, 0x6f, 0x1b, 0x4e, 0xea, 0x66, 0xff, 0xe9, 0x57, 0xcf, 0xeb, 0xc6, 0xd7, 0xcf, 0xeb, + 0xc6, 0xbf, 0x9e, 0xd7, 0x8d, 0xcf, 0x5f, 0xd4, 0xa7, 0xbe, 0x7e, 0x51, 0x9f, 0xfa, 0xe7, 0x8b, + 0xfa, 0xd4, 0xa7, 0xef, 0x5d, 0x06, 0xcd, 0x73, 0x77, 0x37, 0xfb, 0x1a, 0x17, 0xfd, 0xa4, 0xf9, + 0xd9, 0xe8, 0xb7, 0x3e, 0xed, 0xaf, 0x33, 0xab, 0xbb, 0xec, 0xbb, 0xff, 0x0d, 0x00, 0x00, 0xff, + 0xff, 0xbf, 0xd3, 0xf2, 0xea, 0x1c, 0x14, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -2075,6 +2098,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-- @@ -2809,6 +2837,11 @@ func (m *ConsumerValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Height != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x20 + } if m.ConsumerPublicKey != nil { { size, err := m.ConsumerPublicKey.MarshalToSizedBuffer(dAtA[:i]) @@ -3086,6 +3119,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 } @@ -3382,6 +3418,9 @@ func (m *ConsumerValidator) Size() (n int) { l = m.ConsumerPublicKey.Size() n += 1 + l + sovProvider(uint64(l)) } + if m.Height != 0 { + n += 1 + sovProvider(uint64(m.Height)) + } return n } @@ -4994,6 +5033,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:]) @@ -6932,6 +6990,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 Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:])