From 0d6f75f6d1252291903951af827e6dfbf081e069 Mon Sep 17 00:00:00 2001 From: rusttech Date: Fri, 25 Oct 2024 20:53:15 +0800 Subject: [PATCH 01/11] fix: fix slice init length (#2345) --- x/ccv/provider/keeper/consumer_equivocation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/consumer_equivocation.go b/x/ccv/provider/keeper/consumer_equivocation.go index 0fc95f639b..a7c3d2025e 100644 --- a/x/ccv/provider/keeper/consumer_equivocation.go +++ b/x/ccv/provider/keeper/consumer_equivocation.go @@ -184,7 +184,7 @@ func (k Keeper) HandleConsumerMisbehaviour(ctx sdk.Context, consumerId string, m return err } - provAddrs := make([]types.ProviderConsAddress, len(byzantineValidators)) + provAddrs := make([]types.ProviderConsAddress, 0, len(byzantineValidators)) // slash, jail, and tombstone the Byzantine validators for _, v := range byzantineValidators { From dd0929472d70da812537609f6383cb9d1ff3ca99 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:50:48 +0200 Subject: [PATCH 02/11] feat!: Add support for priority validators (#2101) * Start adding priority validators * Add new priority_validator field * Added priority list to the PowerShapingParameters * Add documentation for priority list * Add priority list in local-testnet.sh * Update .changelog/unreleased/features/provider/xxxx-priority-validators.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update x/ccv/provider/keeper/power_shaping.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update x/ccv/provider/keeper/power_shaping_test.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update x/ccv/provider/keeper/power_shaping_test.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update .changelog/unreleased/api-breaking/provider/xxxx-priority-validators.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Fix typo in provider.proto * Remove unnecessary priority list from test in steps_permissionless_ics.go * Remove comment from grpc_query.go * Add e2e test for priority list * Improved e2e test * Fixed some issues * Refactored code logic * Refactored code logic * Renamed files * Removed priority list from migrations.go * Fixed bug for priority list * Added priority list to e2e nightly tests * Update tx.go * Refactored TestCapValidatorSet * Small fixes * Small fixes * Fix PartitionBasedOnPriorityList function * Update contrib/local-testnet.sh Co-authored-by: Marius Poke * Moved 2101-introduce-priority-validators.md into state-breaking * Fixed TestGetConsumerChain * Added test case in TestCapValidatorSet * Revert changes made by the merge * Removed changes from ADRs that were already implemented * Added a comment in CapValidatorSet function * Removed unnecessary imports --------- Co-authored-by: kirdatatjana Co-authored-by: kirdatatjana <116630536+kirdatatjana@users.noreply.github.com> Co-authored-by: Marius Poke --- .../2101-introduce-priority-validators.md | 2 + .../2101-introduce-priority-validators.md | 2 + .github/workflows/nightly-e2e.yml | 17 + contrib/local-testnet.sh | 3 +- docs/docs/build/modules/02-provider.md | 13 +- docs/docs/consumer-development/onboarding.md | 4 +- docs/docs/features/power-shaping.md | 4 + .../ccv/provider/v1/provider.proto | 152 +++++--- .../ccv/provider/v1/query.proto | 5 + tests/e2e/actions.go | 9 + tests/e2e/main.go | 7 + tests/e2e/steps_partial_set_security.go | 184 +++++++++ testutil/keeper/unit_test_helpers.go | 1 + x/ccv/provider/client/cli/tx.go | 6 +- x/ccv/provider/client/legacy_proposals.go | 2 + x/ccv/provider/keeper/consumer_lifecycle.go | 1 + .../keeper/consumer_lifecycle_test.go | 5 + x/ccv/provider/keeper/grpc_query.go | 7 + x/ccv/provider/keeper/grpc_query_test.go | 17 +- x/ccv/provider/keeper/power_shaping.go | 116 +++++- x/ccv/provider/keeper/power_shaping_test.go | 237 +++++++++--- x/ccv/provider/keeper/validator_set_update.go | 4 +- x/ccv/provider/types/keys.go | 16 + x/ccv/provider/types/keys_test.go | 3 + x/ccv/provider/types/msg.go | 3 + x/ccv/provider/types/msg_test.go | 4 + x/ccv/provider/types/provider.pb.go | 346 ++++++++++------- x/ccv/provider/types/query.pb.go | 362 ++++++++++-------- 28 files changed, 1106 insertions(+), 426 deletions(-) create mode 100644 .changelog/unreleased/features/provider/2101-introduce-priority-validators.md create mode 100644 .changelog/unreleased/state-breaking/2101-introduce-priority-validators.md diff --git a/.changelog/unreleased/features/provider/2101-introduce-priority-validators.md b/.changelog/unreleased/features/provider/2101-introduce-priority-validators.md new file mode 100644 index 0000000000..a90e0a6e26 --- /dev/null +++ b/.changelog/unreleased/features/provider/2101-introduce-priority-validators.md @@ -0,0 +1,2 @@ +- Allow consumer chains to specify a list of priority validators that are included in the validator set before other validators are considered + ([\#2101](https://github.com/cosmos/interchain-security/pull/2101)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/2101-introduce-priority-validators.md b/.changelog/unreleased/state-breaking/2101-introduce-priority-validators.md new file mode 100644 index 0000000000..a90e0a6e26 --- /dev/null +++ b/.changelog/unreleased/state-breaking/2101-introduce-priority-validators.md @@ -0,0 +1,2 @@ +- Allow consumer chains to specify a list of priority validators that are included in the validator set before other validators are considered + ([\#2101](https://github.com/cosmos/interchain-security/pull/2101)) \ No newline at end of file diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index efac898687..2751aa598f 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -277,6 +277,22 @@ jobs: go-version: "1.22" # The Go version to download (if necessary) and use. - name: E2E partial set security denylist run: go run ./tests/e2e/... --tc partial-set-security-validators-denylisted + partial-set-security-validators-prioritylisted-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.22" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.22" # The Go version to download (if necessary) and use. + - name: E2E partial set security prioritylist + run: go run ./tests/e2e/... --tc partial-set-security-validators-prioritylisted partial-set-security-modification-proposal: runs-on: ubuntu-latest timeout-minutes: 20 @@ -376,6 +392,7 @@ jobs: - partial-set-security-validators-power-cap-test - partial-set-security-validators-allowlisted-test - partial-set-security-validators-denylisted-test + - partial-set-security-validators-prioritylisted-test - partial-set-security-modification-proposal - active-set-changes-test - permissionless-basic-test diff --git a/contrib/local-testnet.sh b/contrib/local-testnet.sh index 3aebbbe262..a321bfe7f1 100755 --- a/contrib/local-testnet.sh +++ b/contrib/local-testnet.sh @@ -317,7 +317,8 @@ tee ${PROV_NODE_DIR}/consumer_prop.json< []byte{}`, wi Format: `byte(40) | len(consumerId) | []byte(consumerId) -> uint64` +#### Prioritylist + +`Prioritylist` is the list of provider validators that have priority to validate a given consumer chain. + +Format: `byte(56) | len(consumerId) | []byte(consumerId) | addr -> []byte{}`, with `addr` the validator's consensus address on the provider chain. + ### Validator Set Updates #### ValidatorSetUpdateId @@ -1089,6 +1095,7 @@ Output: chains: - allow_inactive_vals: true allowlist: [] + prioritylist: [] chain_id: pion-1 client_id: 07-tendermint-0 consumer_id: "0" @@ -1554,6 +1561,7 @@ power_shaping_params: top_N: 100 validator_set_cap: 0 validators_power_cap: 0 + prioritylist: [] ``` @@ -1679,8 +1687,9 @@ where `update-consumer-msg.json` contains: "validator_set_cap": 50, "allowlist":["cosmosvalcons1l9qq4m300z8c5ez86ak2mp8znftewkwgjlxh88"], "denylist":[], - "min_stake": 1000, - "allow_inactive_vals":true + "min_stake": "1000", + "allow_inactive_vals":true, + "prioritylist":[] }, "allowlisted_reward_denoms": { "denoms": ["ibc/0025F8A87464A471E66B234C4F93AEC5B4DA3D42D7986451A059273426290DD5"] diff --git a/docs/docs/consumer-development/onboarding.md b/docs/docs/consumer-development/onboarding.md index 1e48c7366f..1613a2f268 100644 --- a/docs/docs/consumer-development/onboarding.md +++ b/docs/docs/consumer-development/onboarding.md @@ -125,7 +125,9 @@ Example of power-shaping parameters: // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. "min_stake": 0, // Corresponds to whether inactive validators are allowed to validate the consumer chain. - "allow_inactive_vals": false + "allow_inactive_vals": false, + // Corresponds to a list of provider consensus addresses of validators that have priority + "prioritylist": [], } ``` diff --git a/docs/docs/features/power-shaping.md b/docs/docs/features/power-shaping.md index 7bfad38588..08b4e94cd6 100644 --- a/docs/docs/features/power-shaping.md +++ b/docs/docs/features/power-shaping.md @@ -80,6 +80,10 @@ This can be useful for chains that want to have a larger validator set than the Consumer chains that enable this feature should strongly consider setting a minimum validator stake to ensure that only validators with some reputation/stake can validate the chain. By default, this parameter is set to `false`, i.e., validators outside of the provider's active set are not eligible to opt in. +### Prioritylist + +The consumer chain can specify a priority list of validators for participation in the validator set. Validators on the priority list are considered first when forming the consumer chain's validator set. If a priority list isn't set, the remaining slots are filled based on validator power. + ## Setting Power Shaping Parameters All the power shaping parameters can be set by the consumer chain in the `MsgCreateConsumer` or `MsgUpdateConsumer` messages. diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index e2e6b32c30..138e5fa23b 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -2,24 +2,24 @@ syntax = "proto3"; package interchain_security.ccv.provider.v1; -option go_package = "github.com/cosmos/interchain-security/v6/x/ccv/provider/types"; - -import "interchain_security/ccv/v1/wire.proto"; +import "amino/amino.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/evidence/v1beta1/evidence.proto"; +import "cosmos_proto/cosmos.proto"; import "gogoproto/gogo.proto"; -import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; import "ibc/core/client/v1/client.proto"; import "ibc/lightclients/tendermint/v1/tendermint.proto"; +import "interchain_security/ccv/v1/wire.proto"; import "tendermint/crypto/keys.proto"; -import "cosmos/evidence/v1beta1/evidence.proto"; -import "cosmos/base/v1beta1/coin.proto"; -import "cosmos_proto/cosmos.proto"; -import "amino/amino.proto"; + +option go_package = "github.com/cosmos/interchain-security/v6/x/ccv/provider/types"; // // Note any type defined in this file is ONLY used internally to the provider CCV module. -// These schemas can change with proper consideration of compatibility or migration. -// +// These schemas can change with proper consideration of compatibility or migration. +// // WARNING: This message is deprecated in favor of `MsgCreateConsumer`. // ConsumerAdditionProposal is a governance proposal on the provider chain to @@ -44,7 +44,7 @@ message ConsumerAdditionProposal { // the proposed initial height of new consumer chain. // For a completely new chain, this will be {0,1}. However, it may be // different if this is a chain that is converting to a consumer chain. - ibc.core.client.v1.Height initial_height = 4 [ (gogoproto.nullable) = false ]; + ibc.core.client.v1.Height initial_height = 4 [(gogoproto.nullable) = false]; // The hash of the consumer chain genesis state without the consumer CCV // module genesis params. It is used for off-chain confirmation of // genesis.json validity by validators and other parties. @@ -56,19 +56,27 @@ message ConsumerAdditionProposal { // spawn time is the time on the provider chain at which the consumer chain // genesis is finalized and all validators will be responsible for starting // their consumer chain validator node. - google.protobuf.Timestamp spawn_time = 7 - [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; + google.protobuf.Timestamp spawn_time = 7 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = false + ]; // Unbonding period for the consumer, // which should be smaller than that of the provider in general. - google.protobuf.Duration unbonding_period = 8 - [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; + google.protobuf.Duration unbonding_period = 8 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; // Sent CCV related IBC packets will timeout after this duration - google.protobuf.Duration ccv_timeout_period = 9 - [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; + google.protobuf.Duration ccv_timeout_period = 9 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; // Sent transfer related IBC packets will timeout after this duration - google.protobuf.Duration transfer_timeout_period = 10 - [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; + google.protobuf.Duration transfer_timeout_period = 10 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; // The fraction of tokens allocated to the consumer redistribution address // during distribution events. The fraction is a string representing a // decimal number. For example "0.75" would represent 75%. @@ -124,7 +132,6 @@ message ConsumerRemovalProposal { option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content"; option deprecated = true; - // the title of the proposal string title = 1; // the description of the proposal @@ -133,8 +140,10 @@ message ConsumerRemovalProposal { string chain_id = 3; // the time on the provider chain at which all validators are responsible to // stop their consumer chain validator node - google.protobuf.Timestamp stop_time = 4 - [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; + google.protobuf.Timestamp stop_time = 4 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = false + ]; } // WARNING: This message is deprecated in favor of `MsgUpdateConsumer`. @@ -174,11 +183,10 @@ message ConsumerModificationProposal { bool allow_inactive_vals = 10; } - // EquivocationProposal is a governance proposal on the provider chain to // punish a validator for equivocation on a consumer chain. -// -// This type is only used internally to the consumer CCV module. +// +// This type is only used internally to the consumer CCV module. // WARNING: This message is deprecated now that equivocations can be submitted // and verified automatically on the provider. (see SubmitConsumerDoubleVoting in proto/interchain-security/ccv/provider/v1/tx.proto). message EquivocationProposal { @@ -213,10 +221,12 @@ message ChangeRewardDenomsProposal { message GlobalSlashEntry { // Block time that slash packet was received by provider chain. // This field is used for store key iteration ordering. - google.protobuf.Timestamp recv_time = 1 - [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; + google.protobuf.Timestamp recv_time = 1 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = false + ]; // The consumer that sent a slash packet. - string consumer_chain_id = 2 [ (gogoproto.customname) = "ConsumerChainID" ]; + string consumer_chain_id = 2 [(gogoproto.customname) = "ConsumerChainID"]; // The IBC sequence number of the recv packet. // This field is used in the store key to ensure uniqueness. uint64 ibc_seq_num = 3; @@ -230,7 +240,6 @@ message GlobalSlashEntry { // Params defines the parameters for CCV Provider module message Params { - // Reserve 4th slot for removed init_timeout_period param reserved 4; @@ -245,12 +254,16 @@ message Params { // client's TrustingPeriod from the chain defined UnbondingPeriod string trusting_period_fraction = 2; // Sent IBC packets will timeout after this duration - google.protobuf.Duration ccv_timeout_period = 3 - [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; + google.protobuf.Duration ccv_timeout_period = 3 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; // The period for which the slash meter is replenished - google.protobuf.Duration slash_meter_replenish_period = 6 - [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; + google.protobuf.Duration slash_meter_replenish_period = 6 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; // The fraction of total voting power that is replenished to the slash meter // every replenish period. This param also serves as a maximum fraction of @@ -258,23 +271,24 @@ message Params { string slash_meter_replenish_fraction = 7; // The fee required to be paid to add a reward denom - cosmos.base.v1beta1.Coin consumer_reward_denom_registration_fee = 9 - [ (gogoproto.nullable) = false ]; + cosmos.base.v1beta1.Coin consumer_reward_denom_registration_fee = 9 [(gogoproto.nullable) = false]; // 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; - + // The maximal number of validators that will be passed // to the consensus engine on the provider. int64 max_provider_consensus_validators = 12; } // SlashAcks contains cons addresses of consumer chain validators -// successfully slashed on the provider chain. -message SlashAcks { repeated string addresses = 1; } +// successfully slashed on the provider chain. +message SlashAcks { + repeated string addresses = 1; +} // ConsumerAdditionProposals holds pending governance proposals on the provider // chain to spawn a new chain. @@ -291,21 +305,22 @@ message ConsumerRemovalProposals { } // AddressList contains a list of consensus addresses -message AddressList { repeated bytes addresses = 1; } +message AddressList { + repeated bytes addresses = 1; +} // WARNING: This message is deprecated and is not used. // ChannelToChain is used to map a CCV channel ID to the consumer chainID message ChannelToChain { option deprecated = true; - + string channel_id = 1; string chain_id = 2; } // ValidatorSetChangePackets is a pb list of ccv.ValidatorSetChangePacketData. message ValidatorSetChangePackets { - repeated interchain_security.ccv.v1.ValidatorSetChangePacketData list = 1 - [ (gogoproto.nullable) = false ]; + repeated interchain_security.ccv.v1.ValidatorSetChangePacketData list = 1 [(gogoproto.nullable) = false]; } // @@ -340,15 +355,17 @@ message ValidatorByConsumerAddr { // ConsumerAddrsToPruneV2: (chainID, pruneTs time.Time) -> consumerAddrs AddressList message ConsumerAddrsToPruneV2 { string chain_id = 1; - google.protobuf.Timestamp prune_ts = 2 - [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; + google.protobuf.Timestamp prune_ts = 2 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = false + ]; AddressList consumer_addrs = 3; } // ConsensusValidator is used to express a validator that -// should be validating on a chain. +// should be validating on a chain. // It contains relevant info for -// a validator that is expected to validate on +// a validator that is expected to validate on // either the provider or a consumer chain. message ConsensusValidator { // validator's consensus address on the provider chain @@ -363,13 +380,14 @@ message ConsensusValidator { // 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 +// to the consumer rewards pool. It is used to allocate the tokens to the consumer // opted-in validators and the community pool during BeginBlock. message ConsumerRewardsAllocation { repeated cosmos.base.v1beta1.DecCoin rewards = 1 [ - (gogoproto.nullable) = false, - (amino.dont_omitempty) = true, + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins" ]; } @@ -393,7 +411,7 @@ message ConsumerInitializationParameters { // the proposed initial height of new consumer chain. // For a completely new chain, this will be {0,1}. However, it may be // different if this is a chain that is converting to a consumer chain. - ibc.core.client.v1.Height initial_height = 1 [ (gogoproto.nullable) = false ]; + ibc.core.client.v1.Height initial_height = 1 [(gogoproto.nullable) = false]; // The hash of the consumer chain genesis state without the consumer CCV // module genesis params. It is used for off-chain confirmation of // genesis.json validity by validators and other parties. @@ -405,11 +423,16 @@ message ConsumerInitializationParameters { // spawn time is the time on the provider chain at which the consumer chain // genesis is finalized and all validators will be responsible for starting // their consumer chain validator node. - google.protobuf.Timestamp spawn_time = 4 [ (gogoproto.nullable) = false, (gogoproto.stdtime) = true ]; + google.protobuf.Timestamp spawn_time = 4 [ + (gogoproto.nullable) = false, + (gogoproto.stdtime) = true + ]; // Unbonding period for the consumer, // which should be smaller than that of the provider in general. - google.protobuf.Duration unbonding_period = 5 [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; - + google.protobuf.Duration unbonding_period = 5 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; // ---------- ---------- ---------- // Following fields are used to construct the consumer genesis of the to-be-launched consumer chain @@ -417,9 +440,15 @@ message ConsumerInitializationParameters { // ---------- ---------- ---------- // Sent CCV related IBC packets will timeout after this duration - google.protobuf.Duration ccv_timeout_period = 6 [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; + google.protobuf.Duration ccv_timeout_period = 6 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; // Sent transfer related IBC packets will timeout after this duration - google.protobuf.Duration transfer_timeout_period = 7 [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; + google.protobuf.Duration transfer_timeout_period = 7 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; // The fraction of tokens allocated to the consumer redistribution address // during distribution events. The fraction is a string representing a // decimal number. For example "0.75" would represent 75%. @@ -489,11 +518,18 @@ message PowerShapingParameters { uint64 min_stake = 6; // Corresponds to whether inactive validators are allowed to validate the consumer chain. bool allow_inactive_vals = 7; + // Corresponds to a list of provider consensus addresses of validators that should have PRIORITY to validate on the consumer chain, + // meaning as long as they are eligible/opted in to validate on the consumer chain, the validator set will be + // filled with these validators first, and other validators will be added to the validator set only if there are + // not enough eligible priority validators. + repeated string prioritylist = 8; } // ConsumerIds contains consumer ids of chains // Used so we can easily (de)serialize slices of strings -message ConsumerIds { repeated string ids = 1; } +message ConsumerIds { + repeated string ids = 1; +} // ConsumerPhase indicates the phases of a consumer chain according to ADR 019 enum ConsumerPhase { @@ -517,4 +553,6 @@ enum ConsumerPhase { } // AllowlistedRewardDenoms corresponds to the denoms allowlisted by a specific consumer id -message AllowlistedRewardDenoms { repeated string denoms = 1; } \ No newline at end of file +message AllowlistedRewardDenoms { + repeated string denoms = 1; +} diff --git a/proto/interchain_security/ccv/provider/v1/query.proto b/proto/interchain_security/ccv/provider/v1/query.proto index 910af58278..e7ce6002eb 100644 --- a/proto/interchain_security/ccv/provider/v1/query.proto +++ b/proto/interchain_security/ccv/provider/v1/query.proto @@ -196,6 +196,11 @@ message Chain { string consumer_id = 13; // the reward denoms allowlisted by this consumer chain AllowlistedRewardDenoms allowlisted_reward_denoms = 14; + // Corresponds to a list of provider consensus addresses of validators that should have PRIORITY to validate on the consumer chain, + // meaning as long as they are eligible/opted in to validate on the consumer chain, the validator set will be + // filled with these validators first, and other validators will be added to the validator set only if there are + // not enough eligible priority validators. + repeated string prioritylist = 15; } message QueryValidatorConsumerAddrRequest { diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 06bc94c6af..f43bf0bc5e 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -289,6 +289,7 @@ type PowerShapingParameters struct { Denylist []string MinStake uint64 AllowInactiveVals bool + Prioritylist []string } func (tr Chain) updateConsumerChain(action UpdateConsumerChainAction, verbose bool) { @@ -320,6 +321,7 @@ func (tr Chain) updateConsumerChain(action UpdateConsumerChainAction, verbose bo Denylist: action.PowerShapingParams.Denylist, MinStake: action.PowerShapingParams.MinStake, AllowInactiveVals: action.PowerShapingParams.AllowInactiveVals, + Prioritylist: action.PowerShapingParams.Prioritylist, } consumerId := tr.testConfig.chainConfigs[action.ConsumerChain].ConsumerId @@ -376,6 +378,7 @@ func (tr Chain) createConsumerChain(action CreateConsumerChainAction, verbose bo Denylist: action.PowerShapingParams.Denylist, MinStake: action.PowerShapingParams.MinStake, AllowInactiveVals: action.PowerShapingParams.AllowInactiveVals, + Prioritylist: action.PowerShapingParams.Prioritylist, } metadata := types.ConsumerMetadata{ @@ -463,6 +466,7 @@ type SubmitConsumerAdditionProposalAction struct { Denylist []string MinStake uint64 AllowInactiveVals bool + Prioritylist []string } func (tr Chain) UpdateConsumer(providerChain ChainID, validator ValidatorID, update types.MsgUpdateConsumer, verbose bool) { @@ -657,6 +661,7 @@ func (tr Chain) submitConsumerAdditionProposal( Denylist: action.Denylist, MinStake: action.MinStake, AllowInactiveVals: action.AllowInactiveVals, + Prioritylist: action.Prioritylist, } update.PowerShapingParameters = &powerShapingParameters tr.UpdateConsumer(action.Chain, action.From, *update, verbose) @@ -757,6 +762,7 @@ func (tr Chain) submitConsumerAdditionLegacyProposal( Denylist: action.Denylist, MinStake: action.MinStake, AllowInactiveVals: action.AllowInactiveVals, + Prioritylist: action.Prioritylist, } bz, err := json.Marshal(prop) @@ -937,6 +943,7 @@ type SubmitConsumerModificationProposalAction struct { AllowInactiveVals bool MinStake uint64 NewOwner string + Prioritylist []string } func (tr Chain) submitConsumerModificationProposal( @@ -962,6 +969,7 @@ func (tr Chain) submitConsumerModificationProposal( ValidatorSetCap: action.ValidatorSetCap, Allowlist: action.Allowlist, Denylist: action.Denylist, + Prioritylist: action.Prioritylist, }, } @@ -1019,6 +1027,7 @@ func (tr Chain) submitConsumerModificationLegacyProposal( ValidatorSetCap: action.ValidatorSetCap, Allowlist: action.Allowlist, Denylist: action.Denylist, + Prioritylist: action.Prioritylist, } bz, err := json.Marshal(prop) diff --git a/tests/e2e/main.go b/tests/e2e/main.go index 7b066a2020..e8e3e0640a 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -192,6 +192,12 @@ var stepChoices = map[string]StepChoice{ description: "test partial set security for an Opt-In chain that has a validator denylisted", testConfig: DefaultTestCfg, }, + "partial-set-security-validators-prioritylisted": { + name: "partial-set-security-validators-prioritylisted", + steps: stepsValidatorsPrioritylistedChain(), + description: "test partial set security for an Opt-In chain that has has some validators prioritylisted", + testConfig: DefaultTestCfg, + }, "partial-set-security-modification-proposal": { name: "partial-set-security-modification-proposal", steps: stepsModifyChain(), @@ -360,6 +366,7 @@ func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVe "partial-set-security-validators-power-cap", "partial-set-security-validators-allowlisted", "partial-set-security-validators-denylisted", + "partial-set-security-validators-prioritylisted", "partial-set-security-modification-proposal", "active-set-changes", "inactive-provider-validators-on-consumer", diff --git a/tests/e2e/steps_partial_set_security.go b/tests/e2e/steps_partial_set_security.go index 6b23d675c2..06d41bf4d9 100644 --- a/tests/e2e/steps_partial_set_security.go +++ b/tests/e2e/steps_partial_set_security.go @@ -1840,6 +1840,190 @@ func stepsValidatorsDenylistedChain() []Step { return s } +// stepsValidatorsPrioritylistedChain starts a provider chain and an Opt-In chain with an prioritylist +func stepsValidatorsPrioritylistedChain() []Step { + s := []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: CreateConsumerChainAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + ConsumerChain: ChainID("consu"), + InitParams: &InitializationParameters{ + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + SpawnTime: uint(time.Minute * 10), + }, + PowerShapingParams: &PowerShapingParameters{ + TopN: 0, + ValidatorSetCap: 2, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ProposedConsumerChains: &[]string{"consu"}, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: AssignConsumerPubKeyAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, + ReconfigureNode: true, + }, + State: State{}, + }, + { + Action: UpdateConsumerChainAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + ConsumerChain: ChainID("consu"), + InitParams: &InitializationParameters{ + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + SpawnTime: 0, + }, + PowerShapingParams: &PowerShapingParameters{ + TopN: 0, + ValidatorSetCap: 2, + Prioritylist: []string{ + "cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq", //alice + }, + }, + }, + State: State{}, + }, + { + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 0, // Bob is not in the validator set due to ValidatorSetCap and priority list + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ClientA: 0, + ClientB: 0, + }, + State: State{}, + }, + { + Action: AddIbcChannelAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ConnectionA: 0, + PortA: "consumer", + PortB: "provider", + Order: "ordered", + }, + State: State{}, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 0, + ValidatorID("carol"): 300, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + } + + return s +} + // stepsModifyChain issues multiple `ConsumerModificationProposal`s on a consumer chain to assert that indeed // partial-set security parameters can be changed. func stepsModifyChain() []Step { diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index 929ce7be30..b6038025ed 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -309,6 +309,7 @@ func GetTestPowerShapingParameters() providertypes.PowerShapingParameters { Denylist: nil, MinStake: 0, AllowInactiveVals: false, + Prioritylist: nil, } } diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index ec694fd49e..52a53f3ebe 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -254,7 +254,8 @@ where create_consumer.json has the following structure: "allowlist": ["cosmosvalcons..."], "denylist": ["cosmosvalcons..."], "min_stake": 0, - "allow_inactive_vals": false + "allow_inactive_vals": false, + "prioritylist": ["cosmosvalcons..."] }, "allowlisted_reward_denoms": { "denoms": ["ibc/...", "ibc/..."] @@ -352,7 +353,8 @@ where update_consumer.json has the following structure: "allowlist": ["cosmosvalcons..."], "denylist": ["cosmosvalcons..."], "min_stake": 0, - "allow_inactive_vals": false + "allow_inactive_vals": false, + "prioritylist": ["cosmosvalcons..."] }, "allowlisted_reward_denoms": { "denoms": ["ibc/...", "ibc/..."] diff --git a/x/ccv/provider/client/legacy_proposals.go b/x/ccv/provider/client/legacy_proposals.go index af96ec054b..8993ad9e73 100644 --- a/x/ccv/provider/client/legacy_proposals.go +++ b/x/ccv/provider/client/legacy_proposals.go @@ -43,6 +43,7 @@ type ConsumerAdditionProposalJSON struct { Denylist []string `json:"denylist"` MinStake uint64 `json:"min_stake"` AllowInactiveVals bool `json:"allow_inactive_vals"` + Prioritylist []string `json:"prioritylist"` } type ConsumerAdditionProposalReq struct { @@ -176,6 +177,7 @@ type ConsumerModificationProposalJSON struct { Denylist []string `json:"denylist"` MinStake uint64 `json:"min_stake"` AllowInactiveVals bool `json:"allow_inactive_vals"` + Prioritylist []string `json:"prioritylist"` Deposit string `json:"deposit"` } diff --git a/x/ccv/provider/keeper/consumer_lifecycle.go b/x/ccv/provider/keeper/consumer_lifecycle.go index 7bde45df8a..51d2120f89 100644 --- a/x/ccv/provider/keeper/consumer_lifecycle.go +++ b/x/ccv/provider/keeper/consumer_lifecycle.go @@ -486,6 +486,7 @@ func (k Keeper) DeleteConsumerChain(ctx sdk.Context, consumerId string) (err err k.DeleteDenylist(ctx, consumerId) k.DeleteAllOptedIn(ctx, consumerId) k.DeleteConsumerValSet(ctx, consumerId) + k.DeletePrioritylist(ctx, consumerId) k.DeleteConsumerRemovalTime(ctx, consumerId) diff --git a/x/ccv/provider/keeper/consumer_lifecycle_test.go b/x/ccv/provider/keeper/consumer_lifecycle_test.go index 200c2b949f..f90c359e4c 100644 --- a/x/ccv/provider/keeper/consumer_lifecycle_test.go +++ b/x/ccv/provider/keeper/consumer_lifecycle_test.go @@ -226,6 +226,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { ValidatorSetCap: 0, Allowlist: []string{}, Denylist: []string{}, + Prioritylist: []string{}, }, { Top_N: 50, @@ -233,6 +234,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { ValidatorSetCap: 0, Allowlist: []string{}, Denylist: []string{}, + Prioritylist: []string{}, }, { Top_N: 50, @@ -240,6 +242,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { ValidatorSetCap: 0, Allowlist: []string{}, Denylist: []string{}, + Prioritylist: []string{}, }, { Top_N: 0, @@ -247,6 +250,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { ValidatorSetCap: 0, Allowlist: []string{}, Denylist: []string{}, + Prioritylist: []string{}, }, { Top_N: 0, @@ -254,6 +258,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { ValidatorSetCap: 0, Allowlist: []string{}, Denylist: []string{}, + Prioritylist: []string{}, }, } diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index 9a16e8c5f9..dfc6b06501 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -114,6 +114,12 @@ func (k Keeper) GetConsumerChain(ctx sdk.Context, consumerId string) (types.Chai strDenylist[i] = addr.String() } + prioritylist := k.GetPriorityList(ctx, consumerId) + strPrioritylist := make([]string, len(prioritylist)) + for i, addr := range prioritylist { + strPrioritylist[i] = addr.String() + } + metadata, err := k.GetConsumerMetadata(ctx, consumerId) if err != nil { return types.Chain{}, fmt.Errorf("cannot find metadata (%s): %s", consumerId, err.Error()) @@ -139,6 +145,7 @@ func (k Keeper) GetConsumerChain(ctx sdk.Context, consumerId string) (types.Chai MinStake: powerShapingParameters.MinStake, ConsumerId: consumerId, AllowlistedRewardDenoms: &types.AllowlistedRewardDenoms{Denoms: allowlistedRewardDenoms}, + Prioritylist: strPrioritylist, }, nil } diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index a8fe617f53..5f664a331a 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -451,7 +451,12 @@ func TestGetConsumerChain(t *testing.T) { {types.NewProviderConsAddress([]byte("providerAddr6"))}, {}, } - + prioritylists := [][]types.ProviderConsAddress{ + {}, + {types.NewProviderConsAddress([]byte("providerAddr1")), types.NewProviderConsAddress([]byte("providerAddr2"))}, + {types.NewProviderConsAddress([]byte("providerAddr3"))}, + {}, + } allowInactiveVals := []bool{true, false, true, false} minStakes := []math.Int{ @@ -490,6 +495,9 @@ func TestGetConsumerChain(t *testing.T) { for _, addr := range denylists[i] { pk.SetDenylist(ctx, consumerID, addr) } + for _, addr := range prioritylists[i] { + pk.SetPrioritylist(ctx, consumerID, addr) + } strAllowlist := make([]string, len(allowlists[i])) for j, addr := range allowlists[i] { strAllowlist[j] = addr.String() @@ -500,6 +508,11 @@ func TestGetConsumerChain(t *testing.T) { strDenylist[j] = addr.String() } + strPrioritylist := make([]string, len(prioritylists[i])) + for j, addr := range prioritylists[i] { + strPrioritylist[j] = addr.String() + } + metadataLists = append(metadataLists, types.ConsumerMetadata{Name: chainIDs[i]}) pk.SetConsumerMetadata(ctx, consumerID, metadataLists[i]) @@ -524,6 +537,7 @@ func TestGetConsumerChain(t *testing.T) { MinStake: minStakes[i].Uint64(), ConsumerId: consumerIDs[i], AllowlistedRewardDenoms: allowlistedRewardDenoms[i], + Prioritylist: strPrioritylist, }) } @@ -674,6 +688,7 @@ func TestQueryConsumerChains(t *testing.T) { Metadata: metadata, ConsumerId: consumerId, AllowlistedRewardDenoms: &types.AllowlistedRewardDenoms{Denoms: []string{}}, + Prioritylist: []string{}, } consumerIds[i] = consumerId consumers[i] = &c diff --git a/x/ccv/provider/keeper/power_shaping.go b/x/ccv/provider/keeper/power_shaping.go index 87624922b0..1c789ce30f 100644 --- a/x/ccv/provider/keeper/power_shaping.go +++ b/x/ccv/provider/keeper/power_shaping.go @@ -88,7 +88,7 @@ func (k Keeper) UpdateMinimumPowerInTopN(ctx sdk.Context, consumerId string, old } // CapValidatorSet caps the provided `validators` if chain with `consumerId` is an Opt In chain with a validator-set cap. -// If cap is `k`, `CapValidatorSet` returns the first `k` validators from `validators` with the highest power. +// If cap is `k`, `CapValidatorSet` returns the first `k` validators from `validators`. func (k Keeper) CapValidatorSet( ctx sdk.Context, powerShapingParameters types.PowerShapingParameters, @@ -101,10 +101,7 @@ func (k Keeper) CapValidatorSet( validatorSetCap := powerShapingParameters.ValidatorSetCap if validatorSetCap != 0 && int(validatorSetCap) < len(validators) { - sort.Slice(validators, func(i, j int) bool { - return validators[i].Power > validators[j].Power - }) - + // validators are already sorted by priority and power return validators[:int(validatorSetCap)] } else { return validators @@ -337,13 +334,16 @@ func (k Keeper) SetConsumerPowerShapingParameters(ctx sdk.Context, consumerId st store.Set(types.ConsumerIdToPowerShapingParametersKey(consumerId), bz) - // update allowlist and denylist indexes if needed + // update allowlist, denylist and prioritylist indexes if needed if !equalStringSlices(oldParameters.Allowlist, parameters.Allowlist) { k.UpdateAllowlist(ctx, consumerId, parameters.Allowlist) } if !equalStringSlices(oldParameters.Denylist, parameters.Denylist) { k.UpdateDenylist(ctx, consumerId, parameters.Denylist) } + if !equalStringSlices(oldParameters.Prioritylist, parameters.Prioritylist) { + k.UpdatePrioritylist(ctx, consumerId, parameters.Prioritylist) + } return nil } @@ -551,3 +551,107 @@ func (k Keeper) DeleteMinimumPowerInTopN( store := ctx.KVStore(k.storeKey) store.Delete(types.MinimumPowerInTopNKey(consumerId)) } + +// SetPrioritylist prioritylists validator with `providerAddr` address on chain `consumerId` +func (k Keeper) SetPrioritylist( + ctx sdk.Context, + consumerId string, + providerAddr types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Set(types.PrioritylistKey(consumerId, providerAddr), []byte{}) +} + +// GetPriorityList returns all prioritylisted validators +func (k Keeper) GetPriorityList( + ctx sdk.Context, + consumerId string, +) (providerConsAddresses []types.ProviderConsAddress) { + store := ctx.KVStore(k.storeKey) + key := types.StringIdWithLenKey(types.PrioritylistKeyPrefix(), consumerId) + iterator := storetypes.KVStorePrefixIterator(store, key) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + providerConsAddresses = append(providerConsAddresses, types.NewProviderConsAddress(iterator.Key()[len(key):])) + } + + return providerConsAddresses +} + +// IsPrioritylisted returns `true` if validator with `providerAddr` has been prioritylisted on chain `consumerId` +func (k Keeper) IsPrioritylisted( + ctx sdk.Context, + consumerId string, + providerAddr types.ProviderConsAddress, +) bool { + store := ctx.KVStore(k.storeKey) + return store.Get(types.PrioritylistKey(consumerId, providerAddr)) != nil +} + +// DeletePrioritylist deletes all prioritylisted validators +func (k Keeper) DeletePrioritylist(ctx sdk.Context, consumerId string) { + store := ctx.KVStore(k.storeKey) + iterator := storetypes.KVStorePrefixIterator(store, types.StringIdWithLenKey(types.PrioritylistKeyPrefix(), consumerId)) + defer iterator.Close() + + keysToDel := [][]byte{} + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) + } + + for _, key := range keysToDel { + store.Delete(key) + } +} + +// IsPrioritylistEmpty returns `true` if no validator is prioritylisted on chain `consumerId` +func (k Keeper) IsPrioritylistEmpty(ctx sdk.Context, consumerId string) bool { + store := ctx.KVStore(k.storeKey) + iterator := storetypes.KVStorePrefixIterator(store, types.StringIdWithLenKey(types.PrioritylistKeyPrefix(), consumerId)) + defer iterator.Close() + + return !iterator.Valid() +} + +// UpdatePrioritylist populates the prioritylist store for the consumer chain with this consumer id +func (k Keeper) UpdatePrioritylist(ctx sdk.Context, consumerId string, prioritylist []string) { + k.DeletePrioritylist(ctx, consumerId) + for _, address := range prioritylist { + consAddr, err := sdk.ConsAddressFromBech32(address) + if err != nil { + continue + } + + k.SetPrioritylist(ctx, consumerId, types.NewProviderConsAddress(consAddr)) + } +} + +// PartitionBasedOnPriorityList filters the priority list to include only validators that can validate the chain +// and splits the validators into priority and non-priority sets. +func (k Keeper) PartitionBasedOnPriorityList(ctx sdk.Context, consumerId string, nextValidators []types.ConsensusValidator) ([]types.ConsensusValidator, []types.ConsensusValidator) { + + priorityValidators := make([]types.ConsensusValidator, 0) + nonPriorityValidators := make([]types.ConsensusValidator, 0) + + // Form priorityValidators + for _, validator := range nextValidators { + addr := types.NewProviderConsAddress(validator.ProviderConsAddr) + if k.IsPrioritylisted(ctx, consumerId, addr) { + priorityValidators = append(priorityValidators, validator) + } else { + // Add remaining validators to nonPriorityValidators + nonPriorityValidators = append(nonPriorityValidators, validator) + } + } + + sort.Slice(priorityValidators, func(i, j int) bool { + return priorityValidators[i].Power > priorityValidators[j].Power + }) + + sort.Slice(nonPriorityValidators, func(i, j int) bool { + return nonPriorityValidators[i].Power > nonPriorityValidators[j].Power + }) + + return priorityValidators, nonPriorityValidators +} diff --git a/x/ccv/provider/keeper/power_shaping_test.go b/x/ccv/provider/keeper/power_shaping_test.go index b0853588ee..20358db3f4 100644 --- a/x/ccv/provider/keeper/power_shaping_test.go +++ b/x/ccv/provider/keeper/power_shaping_test.go @@ -250,74 +250,134 @@ func TestCapValidatorSet(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - validatorA := providertypes.ConsensusValidator{ - ProviderConsAddr: []byte("providerConsAddrA"), - Power: 1, - PublicKey: &crypto.PublicKey{}, - } - - validatorB := providertypes.ConsensusValidator{ - ProviderConsAddr: []byte("providerConsAddrB"), - Power: 2, - PublicKey: &crypto.PublicKey{}, - } - - validatorC := providertypes.ConsensusValidator{ - ProviderConsAddr: []byte("providerConsAddrC"), - Power: 3, - PublicKey: &crypto.PublicKey{}, - } - validators := []providertypes.ConsensusValidator{validatorA, validatorB, validatorC} + valAddrA := "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6" + valAddrB := "cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39" + valAddrC := "cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq" + valAddrD := "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk" + + validatorA := providertypes.ConsensusValidator{ProviderConsAddr: consAddressFromBech32(valAddrA), Power: 1, PublicKey: &crypto.PublicKey{}} + validatorB := providertypes.ConsensusValidator{ProviderConsAddr: consAddressFromBech32(valAddrB), Power: 2, PublicKey: &crypto.PublicKey{}} + validatorC := providertypes.ConsensusValidator{ProviderConsAddr: consAddressFromBech32(valAddrC), Power: 3, PublicKey: &crypto.PublicKey{}} + validatorD := providertypes.ConsensusValidator{ProviderConsAddr: consAddressFromBech32(valAddrD), Power: 4, PublicKey: &crypto.PublicKey{}} + validators := []providertypes.ConsensusValidator{validatorA, validatorB, validatorC, validatorD} + // Initial error check powerShapingParameters, err := providerKeeper.GetConsumerPowerShapingParameters(ctx, CONSUMER_ID) require.Error(t, err) - consumerValidators := providerKeeper.CapValidatorSet(ctx, powerShapingParameters, validators) - require.Equal(t, validators, consumerValidators) - - err = providerKeeper.SetConsumerPowerShapingParameters(ctx, CONSUMER_ID, providertypes.PowerShapingParameters{ - ValidatorSetCap: 0, - }) - require.NoError(t, err) - powerShapingParameters, err = providerKeeper.GetConsumerPowerShapingParameters(ctx, CONSUMER_ID) - require.NoError(t, err) - consumerValidators = providerKeeper.CapValidatorSet(ctx, powerShapingParameters, validators) - require.Equal(t, validators, consumerValidators) + priorityValidators, nonPriorityValidators := providerKeeper.PartitionBasedOnPriorityList(ctx, CONSUMER_ID, validators) + consumerValidators := providerKeeper.CapValidatorSet(ctx, powerShapingParameters, append(priorityValidators, nonPriorityValidators...)) + require.Equal(t, []providertypes.ConsensusValidator{validatorD, validatorC, validatorB, validatorA}, consumerValidators) - err = providerKeeper.SetConsumerPowerShapingParameters(ctx, CONSUMER_ID, providertypes.PowerShapingParameters{ - ValidatorSetCap: 100, - }) - require.NoError(t, err) - powerShapingParameters, err = providerKeeper.GetConsumerPowerShapingParameters(ctx, CONSUMER_ID) - require.NoError(t, err) - consumerValidators = providerKeeper.CapValidatorSet(ctx, powerShapingParameters, validators) - require.Equal(t, validators, consumerValidators) + testCases := []struct { + name string + powerShapingParameters providertypes.PowerShapingParameters + expectedValidators []providertypes.ConsensusValidator + expectError bool + }{ + { + name: "ValidatorSetCap = 0 (no capping)", + powerShapingParameters: providertypes.PowerShapingParameters{ValidatorSetCap: 0}, + expectedValidators: []providertypes.ConsensusValidator{validatorD, validatorC, validatorB, validatorA}, + }, + { + name: "ValidatorSetCap = 0, with priority list", + powerShapingParameters: providertypes.PowerShapingParameters{ + ValidatorSetCap: 0, + Prioritylist: []string{ + valAddrA, + valAddrB, + }, + }, + expectedValidators: []providertypes.ConsensusValidator{validatorB, validatorA, validatorD, validatorC}, + }, + { + name: "ValidatorSetCap > len(validators) (no capping)", + powerShapingParameters: providertypes.PowerShapingParameters{ValidatorSetCap: 100}, + expectedValidators: []providertypes.ConsensusValidator{validatorD, validatorC, validatorB, validatorA}, + }, + { + name: "ValidatorSetCap = 1 (capping to highest power, no priority list)", + powerShapingParameters: providertypes.PowerShapingParameters{ValidatorSetCap: 1}, + expectedValidators: []providertypes.ConsensusValidator{validatorD}, + }, + { + name: "ValidatorSetCap = 2 (capping to two highest power, no priority list)", + powerShapingParameters: providertypes.PowerShapingParameters{ValidatorSetCap: 2}, + expectedValidators: []providertypes.ConsensusValidator{validatorD, validatorC}, + }, + { + name: "ValidatorSetCap = 3 (capping to three highest power, no priority list)", + powerShapingParameters: providertypes.PowerShapingParameters{ValidatorSetCap: 3}, + expectedValidators: []providertypes.ConsensusValidator{validatorD, validatorC, validatorB}, + }, + { + name: "ValidatorSetCap = 2, with priority list", + powerShapingParameters: providertypes.PowerShapingParameters{ + ValidatorSetCap: 2, + Prioritylist: []string{ + valAddrA, + valAddrB, + }, + }, + expectedValidators: []providertypes.ConsensusValidator{validatorB, validatorA}, + }, + { + name: "ValidatorSetCap = 3, with partial priority list", + powerShapingParameters: providertypes.PowerShapingParameters{ + ValidatorSetCap: 3, + Prioritylist: []string{valAddrA}, + }, + expectedValidators: []providertypes.ConsensusValidator{validatorA, validatorD, validatorC}, + }, + { + name: "All validators in priority list", + powerShapingParameters: providertypes.PowerShapingParameters{ + ValidatorSetCap: 4, + Prioritylist: []string{ + valAddrC, + valAddrA, + valAddrD, + valAddrB, + }, + }, + expectedValidators: []providertypes.ConsensusValidator{validatorD, validatorC, validatorB, validatorA}, + }, + { + name: "ValidatorSetCap = 1 (capping to highest power, with priority list)", + powerShapingParameters: providertypes.PowerShapingParameters{ + ValidatorSetCap: 1, + Prioritylist: []string{valAddrA}, + }, + expectedValidators: []providertypes.ConsensusValidator{validatorA}, + }, + } - err = providerKeeper.SetConsumerPowerShapingParameters(ctx, CONSUMER_ID, providertypes.PowerShapingParameters{ - ValidatorSetCap: 1, - }) - require.NoError(t, err) - powerShapingParameters, err = providerKeeper.GetConsumerPowerShapingParameters(ctx, CONSUMER_ID) - require.NoError(t, err) - consumerValidators = providerKeeper.CapValidatorSet(ctx, powerShapingParameters, validators) - require.Equal(t, []providertypes.ConsensusValidator{validatorC}, consumerValidators) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := providerKeeper.SetConsumerPowerShapingParameters(ctx, CONSUMER_ID, tc.powerShapingParameters) + require.NoError(t, err) - err = providerKeeper.SetConsumerPowerShapingParameters(ctx, CONSUMER_ID, providertypes.PowerShapingParameters{ - ValidatorSetCap: 2, - }) - require.NoError(t, err) - powerShapingParameters, err = providerKeeper.GetConsumerPowerShapingParameters(ctx, CONSUMER_ID) - require.NoError(t, err) - consumerValidators = providerKeeper.CapValidatorSet(ctx, powerShapingParameters, validators) - require.Equal(t, []providertypes.ConsensusValidator{validatorC, validatorB}, consumerValidators) + powerShapingParameters, err := providerKeeper.GetConsumerPowerShapingParameters(ctx, CONSUMER_ID) + if tc.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.powerShapingParameters, powerShapingParameters) + } + priorityValidators, nonPriorityValidators := providerKeeper.PartitionBasedOnPriorityList(ctx, CONSUMER_ID, validators) + consumerValidators := providerKeeper.CapValidatorSet(ctx, powerShapingParameters, append(priorityValidators, nonPriorityValidators...)) + require.Equal(t, tc.expectedValidators, consumerValidators) + }) + } +} - err = providerKeeper.SetConsumerPowerShapingParameters(ctx, CONSUMER_ID, providertypes.PowerShapingParameters{ - ValidatorSetCap: 3, - }) - require.NoError(t, err) - powerShapingParameters, err = providerKeeper.GetConsumerPowerShapingParameters(ctx, CONSUMER_ID) - require.NoError(t, err) - consumerValidators = providerKeeper.CapValidatorSet(ctx, powerShapingParameters, validators) - require.Equal(t, []providertypes.ConsensusValidator{validatorC, validatorB, validatorA}, consumerValidators) +// Helper function to handle address conversion +func consAddressFromBech32(addr string) sdk.ConsAddress { + consAddr, err := sdk.ConsAddressFromBech32(addr) + if err != nil { + panic(fmt.Sprintf("invalid consensus address: %s", addr)) + } + return consAddr } func TestCapValidatorsPower(t *testing.T) { @@ -745,11 +805,14 @@ func TestConsumerPowerShapingParameters(t *testing.T) { Denylist: []string{consAddrs[2], consAddrs[3]}, MinStake: 234, AllowInactiveVals: true, + Prioritylist: []string{consAddrs[1]}, } expectedAllowlist := []providertypes.ProviderConsAddress{providerConsAddr[0], providerConsAddr[1]} sortProviderConsAddr(expectedAllowlist) expectedDenylist := []providertypes.ProviderConsAddress{providerConsAddr[2], providerConsAddr[3]} sortProviderConsAddr(expectedDenylist) + expectedPrioritylist := []providertypes.ProviderConsAddress{providerConsAddr[1]} + sortProviderConsAddr(expectedPrioritylist) err = providerKeeper.SetConsumerPowerShapingParameters(ctx, consumerId, expectedPowerShapingParameters) require.NoError(t, err) actualPowerShapingParameters, err := providerKeeper.GetConsumerPowerShapingParameters(ctx, consumerId) @@ -757,6 +820,7 @@ func TestConsumerPowerShapingParameters(t *testing.T) { require.Equal(t, expectedPowerShapingParameters, actualPowerShapingParameters) require.Equal(t, expectedAllowlist, providerKeeper.GetAllowList(ctx, consumerId)) require.Equal(t, expectedDenylist, providerKeeper.GetDenyList(ctx, consumerId)) + require.Equal(t, expectedPrioritylist, providerKeeper.GetPriorityList(ctx, consumerId)) // assert that overwriting the current initialization record works expectedPowerShapingParameters = providertypes.PowerShapingParameters{ @@ -767,11 +831,14 @@ func TestConsumerPowerShapingParameters(t *testing.T) { Denylist: []string{consAddrs[2], consAddrs[3]}, MinStake: 567, AllowInactiveVals: false, + Prioritylist: []string{consAddrs[4], consAddrs[5]}, } expectedAllowlist = []providertypes.ProviderConsAddress{providerConsAddr[4], providerConsAddr[5]} sortProviderConsAddr(expectedAllowlist) expectedDenylist = []providertypes.ProviderConsAddress{providerConsAddr[2], providerConsAddr[3]} sortProviderConsAddr(expectedDenylist) + expectedPrioritylist = []providertypes.ProviderConsAddress{providerConsAddr[4], providerConsAddr[5]} + sortProviderConsAddr(expectedAllowlist) err = providerKeeper.SetConsumerPowerShapingParameters(ctx, consumerId, expectedPowerShapingParameters) require.NoError(t, err) actualPowerShapingParameters, err = providerKeeper.GetConsumerPowerShapingParameters(ctx, consumerId) @@ -779,6 +846,7 @@ func TestConsumerPowerShapingParameters(t *testing.T) { require.Equal(t, expectedPowerShapingParameters, actualPowerShapingParameters) require.Equal(t, expectedAllowlist, providerKeeper.GetAllowList(ctx, consumerId)) require.Equal(t, expectedDenylist, providerKeeper.GetDenyList(ctx, consumerId)) + require.Equal(t, expectedPrioritylist, providerKeeper.GetPriorityList(ctx, consumerId)) } // TestAllowlist tests the `SetAllowlist`, `IsAllowlisted`, `DeleteAllowlist`, and `IsAllowlistEmpty` methods @@ -1006,3 +1074,50 @@ func TestUpdateMinimumPowerInTopN(t *testing.T) { require.True(t, found) require.Equal(t, int64(10), minimumPowerInTopN) } + +func TestPrioritylist(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerID := CONSUMER_ID + + // no validator was prioritylisted and hence the prioritylist is empty + require.True(t, providerKeeper.IsPrioritylistEmpty(ctx, consumerID)) + + providerAddr1 := providertypes.NewProviderConsAddress([]byte("providerAddr1")) + providerKeeper.SetPrioritylist(ctx, consumerID, providerAddr1) + require.True(t, providerKeeper.IsPrioritylisted(ctx, consumerID, providerAddr1)) + + // prioritylist is not empty anymore + require.False(t, providerKeeper.IsPrioritylistEmpty(ctx, consumerID)) + + providerAddr2 := providertypes.NewProviderConsAddress([]byte("providerAddr2")) + providerKeeper.SetPrioritylist(ctx, consumerID, providerAddr2) + require.True(t, providerKeeper.IsPrioritylisted(ctx, consumerID, providerAddr2)) + require.False(t, providerKeeper.IsPrioritylistEmpty(ctx, consumerID)) + + providerKeeper.DeletePrioritylist(ctx, consumerID) + require.False(t, providerKeeper.IsPrioritylisted(ctx, consumerID, providerAddr1)) + require.False(t, providerKeeper.IsPrioritylisted(ctx, consumerID, providerAddr2)) + require.True(t, providerKeeper.IsPrioritylistEmpty(ctx, consumerID)) +} + +func TestUpdatePrioritylist(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := "0" + + providerConsAddr1 := "cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq" + consAddr1, _ := sdk.ConsAddressFromBech32(providerConsAddr1) + providerConsAddr2 := "cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39" + consAddr2, _ := sdk.ConsAddressFromBech32(providerConsAddr2) + + providerKeeper.UpdatePrioritylist(ctx, consumerId, []string{providerConsAddr1, providerConsAddr2}) + + expectedPrioritylist := []providertypes.ProviderConsAddress{ + providertypes.NewProviderConsAddress(consAddr1), + providertypes.NewProviderConsAddress(consAddr2), + } + require.Equal(t, expectedPrioritylist, providerKeeper.GetPriorityList(ctx, consumerId)) +} diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index d375d42d8f..265911d5d2 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -233,7 +233,9 @@ func (k Keeper) ComputeNextValidators( return []types.ConsensusValidator{}, err } - nextValidators = k.CapValidatorSet(ctx, powerShapingParameters, nextValidators) + priorityValidators, nonPriorityValidators := k.PartitionBasedOnPriorityList(ctx, consumerId, nextValidators) + + nextValidators = k.CapValidatorSet(ctx, powerShapingParameters, append(priorityValidators, nonPriorityValidators...)) nextValidators = k.CapValidatorsPower(ctx, powerShapingParameters.ValidatorsPowerCap, nextValidators) diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index ca78611fd8..2e712b3e6e 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -154,6 +154,8 @@ const ( ConsumerIdToAllowlistedRewardDenomKeyName = "ConsumerIdToAllowlistedRewardDenomKey" ConsumerRewardsAllocationByDenomKeyName = "ConsumerRewardsAllocationByDenomKey" + + PrioritylistKeyName = "PrioritylistKey" ) // getKeyPrefixes returns a constant map of all the byte prefixes for existing keys @@ -391,6 +393,10 @@ func getKeyPrefixes() map[string]byte { // ConsumerRewardsAllocationByDenomKeyName is the key for storing the consumer rewards for a specific consumer chain and denom ConsumerRewardsAllocationByDenomKeyName: 55, + // PrioritylistKey is the key for storing the mapping from a consumer chain to the set of validators that are + // prioritylisted. + PrioritylistKeyName: 56, + // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO TestPreserveBytePrefix() IN keys_test.go } } @@ -597,6 +603,16 @@ func DenylistKey(consumerId string, providerAddr ProviderConsAddress) []byte { return StringIdAndConsAddrKey(DenylistKeyPrefix(), consumerId, providerAddr.ToSdkConsAddr()) } +// PrioritylistKeyPrefix returns the key prefix for storing consumer chains prioritylists +func PrioritylistKeyPrefix() byte { + return mustGetKeyPrefix(PrioritylistKeyName) +} + +// PrioritylistKey returns the key for storing consumer chains prioritylists +func PrioritylistKey(consumerId string, providerAddr ProviderConsAddress) []byte { + return StringIdAndConsAddrKey(PrioritylistKeyPrefix(), consumerId, providerAddr.ToSdkConsAddr()) +} + // OptedInKeyPrefix returns the key prefix for storing whether a validator is opted in on a consumer chain. func OptedInKeyPrefix() byte { return mustGetKeyPrefix(OptedInKeyName) diff --git a/x/ccv/provider/types/keys_test.go b/x/ccv/provider/types/keys_test.go index 814ece665a..de8156c8ec 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -147,6 +147,8 @@ func TestPreserveBytePrefix(t *testing.T) { i++ require.Equal(t, byte(55), providertypes.ConsumerRewardsAllocationByDenomKey("13", "denom")[0]) i++ + require.Equal(t, byte(56), providertypes.PrioritylistKeyPrefix()) + i++ prefixes := providertypes.GetAllKeyPrefixes() require.Equal(t, len(prefixes), i) @@ -216,6 +218,7 @@ func getAllFullyDefinedKeys() [][]byte { providertypes.ClientIdToConsumerIdKey("clientId"), providertypes.ConsumerIdToAllowlistedRewardDenomKey("13"), providertypes.ConsumerRewardsAllocationByDenomKey("13", "denom"), + providertypes.PrioritylistKey("13", providertypes.NewProviderConsAddress([]byte{0x05})), } } diff --git a/x/ccv/provider/types/msg.go b/x/ccv/provider/types/msg.go index acfdf1bd8b..91a5282c4f 100644 --- a/x/ccv/provider/types/msg.go +++ b/x/ccv/provider/types/msg.go @@ -529,6 +529,9 @@ func ValidatePowerShapingParameters(powerShapingParameters PowerShapingParameter if err := ValidateConsAddressList(powerShapingParameters.Denylist, MaxValidatorCount); err != nil { return errorsmod.Wrapf(ErrInvalidPowerShapingParameters, "Denylist: %s", err.Error()) } + if err := ValidateConsAddressList(powerShapingParameters.Prioritylist, MaxValidatorCount); err != nil { + return errorsmod.Wrapf(ErrInvalidPowerShapingParameters, "Prioritylist: %s", err.Error()) + } return nil } diff --git a/x/ccv/provider/types/msg_test.go b/x/ccv/provider/types/msg_test.go index b0f0f11102..f1eaa3124a 100644 --- a/x/ccv/provider/types/msg_test.go +++ b/x/ccv/provider/types/msg_test.go @@ -502,6 +502,7 @@ func TestMsgUpdateConsumerValidateBasic(t *testing.T) { Denylist: nil, MinStake: 0, AllowInactiveVals: false, + Prioritylist: []string{consAddr1}, }, true, }, @@ -513,6 +514,7 @@ func TestMsgUpdateConsumerValidateBasic(t *testing.T) { ValidatorSetCap: 0, Allowlist: nil, Denylist: nil, + Prioritylist: nil, }, false, }, @@ -526,6 +528,7 @@ func TestMsgUpdateConsumerValidateBasic(t *testing.T) { Denylist: nil, MinStake: 0, AllowInactiveVals: false, + Prioritylist: nil, }, false, }, @@ -539,6 +542,7 @@ func TestMsgUpdateConsumerValidateBasic(t *testing.T) { Denylist: []string{consAddr2, consAddr3}, MinStake: 0, AllowInactiveVals: false, + Prioritylist: []string{consAddr1}, }, true, }, diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index c390ab752b..ae7402200e 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -1699,6 +1699,11 @@ type PowerShapingParameters struct { MinStake uint64 `protobuf:"varint,6,opt,name=min_stake,json=minStake,proto3" json:"min_stake,omitempty"` // Corresponds to whether inactive validators are allowed to validate the consumer chain. AllowInactiveVals bool `protobuf:"varint,7,opt,name=allow_inactive_vals,json=allowInactiveVals,proto3" json:"allow_inactive_vals,omitempty"` + // Corresponds to a list of provider consensus addresses of validators that should have PRIORITY to validate on the consumer chain, + // meaning as long as they are eligible/opted in to validate on the consumer chain, the validator set will be + // filled with these validators first, and other validators will be added to the validator set only if there are + // not enough eligible priority validators. + Prioritylist []string `protobuf:"bytes,8,rep,name=prioritylist,proto3" json:"prioritylist,omitempty"` } func (m *PowerShapingParameters) Reset() { *m = PowerShapingParameters{} } @@ -1783,6 +1788,13 @@ func (m *PowerShapingParameters) GetAllowInactiveVals() bool { return false } +func (m *PowerShapingParameters) GetPrioritylist() []string { + if m != nil { + return m.Prioritylist + } + return nil +} + // ConsumerIds contains consumer ids of chains // Used so we can easily (de)serialize slices of strings type ConsumerIds struct { @@ -1907,149 +1919,150 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 2267 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x59, 0xcb, 0x6f, 0x1b, 0xc9, - 0xd1, 0xd7, 0x88, 0x94, 0x44, 0x16, 0xf5, 0xa0, 0xda, 0x5e, 0x99, 0x92, 0xb5, 0x24, 0xcd, 0xfd, - 0xbc, 0xe0, 0x67, 0xc7, 0xe4, 0x4a, 0x0b, 0x04, 0x86, 0x93, 0x85, 0x41, 0x93, 0xb4, 0x4d, 0x3f, - 0x64, 0x66, 0x48, 0x6b, 0x01, 0xe7, 0x30, 0x68, 0xce, 0xb4, 0xc8, 0x8e, 0xe6, 0xe5, 0xe9, 0x26, - 0x6d, 0xe6, 0x90, 0xf3, 0x22, 0x40, 0x80, 0x4d, 0x4e, 0x8b, 0x5c, 0xb2, 0x40, 0x2e, 0x41, 0x4e, - 0x39, 0x04, 0xf9, 0x03, 0x72, 0xda, 0x04, 0x08, 0xb0, 0xb9, 0xe5, 0xb4, 0x1b, 0xd8, 0x87, 0x1c, - 0x02, 0x24, 0xe7, 0xdc, 0x82, 0xee, 0x79, 0x70, 0xa8, 0x97, 0x69, 0xd8, 0xce, 0x45, 0x9a, 0xae, - 0xfa, 0x55, 0x75, 0x75, 0x77, 0xbd, 0xba, 0x09, 0xbb, 0xd4, 0xe6, 0xc4, 0xd3, 0x07, 0x98, 0xda, - 0x1a, 0x23, 0xfa, 0xd0, 0xa3, 0x7c, 0x5c, 0xd5, 0xf5, 0x51, 0xd5, 0xf5, 0x9c, 0x11, 0x35, 0x88, - 0x57, 0x1d, 0xed, 0x44, 0xdf, 0x15, 0xd7, 0x73, 0xb8, 0x83, 0x3e, 0x38, 0x41, 0xa6, 0xa2, 0xeb, - 0xa3, 0x4a, 0x84, 0x1b, 0xed, 0x6c, 0x5d, 0x3e, 0x4d, 0xf1, 0x68, 0xa7, 0xfa, 0x8c, 0x7a, 0xc4, - 0xd7, 0xb5, 0x75, 0xbe, 0xef, 0xf4, 0x1d, 0xf9, 0x59, 0x15, 0x5f, 0x01, 0xb5, 0xd0, 0x77, 0x9c, - 0xbe, 0x49, 0xaa, 0x72, 0xd4, 0x1b, 0x1e, 0x54, 0x39, 0xb5, 0x08, 0xe3, 0xd8, 0x72, 0x03, 0x40, - 0xfe, 0x28, 0xc0, 0x18, 0x7a, 0x98, 0x53, 0xc7, 0x0e, 0x15, 0xd0, 0x9e, 0x5e, 0xd5, 0x1d, 0x8f, - 0x54, 0x75, 0x93, 0x12, 0x9b, 0x8b, 0x59, 0xfd, 0xaf, 0x00, 0x50, 0x15, 0x00, 0x93, 0xf6, 0x07, - 0xdc, 0x27, 0xb3, 0x2a, 0x27, 0xb6, 0x41, 0x3c, 0x8b, 0xfa, 0xe0, 0xc9, 0x28, 0x10, 0xd8, 0x8e, - 0xf1, 0x75, 0x6f, 0xec, 0x72, 0xa7, 0x7a, 0x48, 0xc6, 0x2c, 0xe0, 0x7e, 0xa8, 0x3b, 0xcc, 0x72, - 0x58, 0x95, 0x88, 0xf5, 0xdb, 0x3a, 0xa9, 0x8e, 0x76, 0x7a, 0x84, 0xe3, 0x9d, 0x88, 0x10, 0xda, - 0x1d, 0xe0, 0x7a, 0x98, 0x4d, 0x30, 0xba, 0x43, 0x43, 0xbb, 0x37, 0x7d, 0xbe, 0xe6, 0xef, 0x88, - 0x3f, 0x08, 0x58, 0xeb, 0xd8, 0xa2, 0xb6, 0x53, 0x95, 0x7f, 0x7d, 0x52, 0xe9, 0x3f, 0x29, 0xc8, - 0xd5, 0x1d, 0x9b, 0x0d, 0x2d, 0xe2, 0xd5, 0x0c, 0x83, 0x8a, 0x0d, 0x68, 0x7b, 0x8e, 0xeb, 0x30, - 0x6c, 0xa2, 0xf3, 0xb0, 0xc0, 0x29, 0x37, 0x49, 0x4e, 0x29, 0x2a, 0xe5, 0xb4, 0xea, 0x0f, 0x50, - 0x11, 0x32, 0x06, 0x61, 0xba, 0x47, 0x5d, 0x01, 0xce, 0xcd, 0x4b, 0x5e, 0x9c, 0x84, 0x36, 0x21, - 0xe5, 0x9f, 0x1a, 0x35, 0x72, 0x09, 0xc9, 0x5e, 0x92, 0xe3, 0x96, 0x81, 0xee, 0xc0, 0x2a, 0xb5, - 0x29, 0xa7, 0xd8, 0xd4, 0x06, 0x44, 0xec, 0x5d, 0x2e, 0x59, 0x54, 0xca, 0x99, 0xdd, 0xad, 0x0a, - 0xed, 0xe9, 0x15, 0xb1, 0xdd, 0x95, 0x60, 0x93, 0x47, 0x3b, 0x95, 0xbb, 0x12, 0x71, 0x2b, 0xf9, - 0xd5, 0x37, 0x85, 0x39, 0x75, 0x25, 0x90, 0xf3, 0x89, 0xe8, 0x12, 0x2c, 0xf7, 0x89, 0x4d, 0x18, - 0x65, 0xda, 0x00, 0xb3, 0x41, 0x6e, 0xa1, 0xa8, 0x94, 0x97, 0xd5, 0x4c, 0x40, 0xbb, 0x8b, 0xd9, - 0x00, 0x15, 0x20, 0xd3, 0xa3, 0x36, 0xf6, 0xc6, 0x3e, 0x62, 0x51, 0x22, 0xc0, 0x27, 0x49, 0x40, - 0x1d, 0x80, 0xb9, 0xf8, 0x99, 0xad, 0x09, 0xdf, 0xc8, 0x2d, 0x05, 0x86, 0xf8, 0x7e, 0x51, 0x09, - 0xfd, 0xa2, 0xd2, 0x0d, 0x1d, 0xe7, 0x56, 0x4a, 0x18, 0xf2, 0xf9, 0xb7, 0x05, 0x45, 0x4d, 0x4b, - 0x39, 0xc1, 0x41, 0x7b, 0x90, 0x1d, 0xda, 0x3d, 0xc7, 0x36, 0xa8, 0xdd, 0xd7, 0x5c, 0xe2, 0x51, - 0xc7, 0xc8, 0xa5, 0xa4, 0xaa, 0xcd, 0x63, 0xaa, 0x1a, 0x81, 0x8b, 0xf9, 0x9a, 0xbe, 0x10, 0x9a, - 0xd6, 0x22, 0xe1, 0xb6, 0x94, 0x45, 0x3f, 0x00, 0xa4, 0xeb, 0x23, 0x69, 0x92, 0x33, 0xe4, 0xa1, - 0xc6, 0xf4, 0xec, 0x1a, 0xb3, 0xba, 0x3e, 0xea, 0xfa, 0xd2, 0x81, 0xca, 0x1f, 0xc2, 0x05, 0xee, - 0x61, 0x9b, 0x1d, 0x10, 0xef, 0xa8, 0x5e, 0x98, 0x5d, 0xef, 0x7b, 0xa1, 0x8e, 0x69, 0xe5, 0x77, - 0xa1, 0xa8, 0x07, 0x0e, 0xa4, 0x79, 0xc4, 0xa0, 0x8c, 0x7b, 0xb4, 0x37, 0x14, 0xb2, 0xda, 0x81, - 0x87, 0x75, 0xe9, 0x23, 0x19, 0xe9, 0x04, 0xf9, 0x10, 0xa7, 0x4e, 0xc1, 0x6e, 0x07, 0x28, 0xf4, - 0x08, 0xfe, 0xaf, 0x67, 0x3a, 0xfa, 0x21, 0x13, 0xc6, 0x69, 0x53, 0x9a, 0xe4, 0xd4, 0x16, 0x65, - 0x4c, 0x68, 0x5b, 0x2e, 0x2a, 0xe5, 0x84, 0x7a, 0xc9, 0xc7, 0xb6, 0x89, 0xd7, 0x88, 0x21, 0xbb, - 0x31, 0x20, 0xba, 0x06, 0x68, 0x40, 0x19, 0x77, 0x3c, 0xaa, 0x63, 0x53, 0x23, 0x36, 0xf7, 0x28, - 0x61, 0xb9, 0x15, 0x29, 0xbe, 0x3e, 0xe1, 0x34, 0x7d, 0x06, 0xba, 0x07, 0x97, 0x4e, 0x9d, 0x54, - 0xd3, 0x07, 0xd8, 0xb6, 0x89, 0x99, 0x5b, 0x95, 0x4b, 0x29, 0x18, 0xa7, 0xcc, 0x59, 0xf7, 0x61, - 0xe8, 0x1c, 0x2c, 0x70, 0xc7, 0xd5, 0xf6, 0x72, 0x6b, 0x45, 0xa5, 0xbc, 0xa2, 0x26, 0xb9, 0xe3, - 0xee, 0xa1, 0x8f, 0xe0, 0xfc, 0x08, 0x9b, 0xd4, 0xc0, 0xdc, 0xf1, 0x98, 0xe6, 0x3a, 0xcf, 0x88, - 0xa7, 0xe9, 0xd8, 0xcd, 0x65, 0x25, 0x06, 0x4d, 0x78, 0x6d, 0xc1, 0xaa, 0x63, 0x17, 0x5d, 0x81, - 0xf5, 0x88, 0xaa, 0x31, 0xc2, 0x25, 0x7c, 0x5d, 0xc2, 0xd7, 0x22, 0x46, 0x87, 0x70, 0x81, 0xdd, - 0x86, 0x34, 0x36, 0x4d, 0xe7, 0x99, 0x49, 0x19, 0xcf, 0xa1, 0x62, 0xa2, 0x9c, 0x56, 0x27, 0x04, - 0xb4, 0x05, 0x29, 0x83, 0xd8, 0x63, 0xc9, 0x3c, 0x27, 0x99, 0xd1, 0x18, 0x5d, 0x84, 0xb4, 0x25, - 0x72, 0x2c, 0xc7, 0x87, 0x24, 0x77, 0xbe, 0xa8, 0x94, 0x93, 0x6a, 0xca, 0xa2, 0x76, 0x47, 0x8c, - 0x51, 0x05, 0xce, 0x49, 0x2d, 0x1a, 0xb5, 0xc5, 0x39, 0x8d, 0x88, 0x36, 0xc2, 0x26, 0xcb, 0xbd, - 0x57, 0x54, 0xca, 0x29, 0x75, 0x5d, 0xb2, 0x5a, 0x01, 0x67, 0x1f, 0x9b, 0xec, 0x46, 0xf9, 0xb3, - 0x2f, 0x0b, 0x73, 0x5f, 0x7c, 0x59, 0x98, 0xfb, 0xf3, 0xef, 0xaf, 0x6d, 0x05, 0xe9, 0xa7, 0xef, - 0x8c, 0x2a, 0x41, 0xaa, 0xaa, 0xd4, 0x1d, 0x9b, 0x13, 0x9b, 0xe7, 0x94, 0xd2, 0x5f, 0x15, 0xb8, - 0x50, 0x8f, 0x5c, 0xc2, 0x72, 0x46, 0xd8, 0x7c, 0x97, 0xa9, 0xa7, 0x06, 0x69, 0x26, 0xce, 0x44, - 0x06, 0x7b, 0xf2, 0x35, 0x82, 0x3d, 0x25, 0xc4, 0x04, 0xe3, 0x46, 0xf1, 0x95, 0x6b, 0xfa, 0xf7, - 0x3c, 0x6c, 0x87, 0x6b, 0x7a, 0xe8, 0x18, 0xf4, 0x80, 0xea, 0xf8, 0x5d, 0xe7, 0xd4, 0xc8, 0xd7, - 0x92, 0x33, 0xf8, 0xda, 0xc2, 0xeb, 0xf9, 0xda, 0xe2, 0x0c, 0xbe, 0xb6, 0x74, 0x96, 0xaf, 0xa5, - 0xce, 0xf2, 0xb5, 0xf4, 0x6c, 0xbe, 0x06, 0xa7, 0xf9, 0xda, 0x7c, 0x4e, 0x29, 0xfd, 0x4a, 0x81, - 0xf3, 0xcd, 0xa7, 0x43, 0x3a, 0x72, 0xde, 0xd2, 0x4e, 0xdf, 0x87, 0x15, 0x12, 0xd3, 0xc7, 0x72, - 0x89, 0x62, 0xa2, 0x9c, 0xd9, 0xbd, 0x5c, 0x09, 0x0e, 0x3e, 0xaa, 0xc7, 0xe1, 0xe9, 0xc7, 0x67, - 0x57, 0xa7, 0x65, 0xa5, 0x85, 0x7f, 0x54, 0x60, 0x4b, 0xe4, 0x85, 0x3e, 0x51, 0xc9, 0x33, 0xec, - 0x19, 0x0d, 0x62, 0x3b, 0x16, 0x7b, 0x63, 0x3b, 0x4b, 0xb0, 0x62, 0x48, 0x4d, 0x1a, 0x77, 0x34, - 0x6c, 0x18, 0xd2, 0x4e, 0x89, 0x11, 0xc4, 0xae, 0x53, 0x33, 0x0c, 0x54, 0x86, 0xec, 0x04, 0xe3, - 0x89, 0x18, 0x13, 0xae, 0x2f, 0x60, 0xab, 0x21, 0x4c, 0x46, 0x1e, 0xb9, 0x91, 0x3f, 0xdb, 0xb5, - 0x4b, 0xff, 0x54, 0x20, 0x7b, 0xc7, 0x74, 0x7a, 0xd8, 0xec, 0x98, 0x98, 0x0d, 0x44, 0xce, 0x1c, - 0x8b, 0x90, 0xf2, 0x48, 0x50, 0xac, 0xa4, 0xf9, 0x33, 0x87, 0x94, 0x10, 0x93, 0xe5, 0xf3, 0x26, - 0xac, 0x47, 0xe5, 0x23, 0x72, 0x70, 0xb9, 0xda, 0x5b, 0xe7, 0x5e, 0x7c, 0x53, 0x58, 0x0b, 0x83, - 0xa9, 0x2e, 0x9d, 0xbd, 0xa1, 0xae, 0xe9, 0x53, 0x04, 0x03, 0xe5, 0x21, 0x43, 0x7b, 0xba, 0xc6, - 0xc8, 0x53, 0xcd, 0x1e, 0x5a, 0x32, 0x36, 0x92, 0x6a, 0x9a, 0xf6, 0xf4, 0x0e, 0x79, 0xba, 0x37, - 0xb4, 0xd0, 0xc7, 0xb0, 0x11, 0x36, 0x95, 0xc2, 0x9b, 0x34, 0x21, 0x2f, 0xb6, 0xcb, 0x93, 0xe1, - 0xb2, 0xac, 0x9e, 0x0b, 0xb9, 0xfb, 0xd8, 0x14, 0x93, 0xd5, 0x0c, 0xc3, 0x2b, 0xfd, 0x6b, 0x01, - 0x16, 0xdb, 0xd8, 0xc3, 0x16, 0x43, 0x5d, 0x58, 0xe3, 0xc4, 0x72, 0x4d, 0xcc, 0x89, 0xe6, 0xb7, - 0x26, 0xc1, 0x4a, 0xaf, 0xca, 0x96, 0x25, 0xde, 0x00, 0x56, 0x62, 0x2d, 0xdf, 0x68, 0xa7, 0x52, - 0x97, 0xd4, 0x0e, 0xc7, 0x9c, 0xa8, 0xab, 0xa1, 0x0e, 0x9f, 0x88, 0xae, 0x43, 0x8e, 0x7b, 0x43, - 0xc6, 0x27, 0x4d, 0xc3, 0xa4, 0x5a, 0xfa, 0x67, 0xbd, 0x11, 0xf2, 0xfd, 0x3a, 0x1b, 0x55, 0xc9, - 0x93, 0xfb, 0x83, 0xc4, 0x9b, 0xf4, 0x07, 0x06, 0x6c, 0x33, 0x71, 0xa8, 0x9a, 0x45, 0xb8, 0xac, - 0xe2, 0xae, 0x49, 0x6c, 0xca, 0x06, 0xa1, 0xf2, 0xc5, 0xd9, 0x95, 0x6f, 0x4a, 0x45, 0x0f, 0x85, - 0x1e, 0x35, 0x54, 0x13, 0xcc, 0x52, 0x87, 0xfc, 0xc9, 0xb3, 0x44, 0x0b, 0x5f, 0x92, 0x0b, 0xbf, - 0x78, 0x82, 0x8a, 0x68, 0xf5, 0x0c, 0x3e, 0x8c, 0x75, 0x1b, 0x22, 0x9a, 0x34, 0xe9, 0xc8, 0x9a, - 0x47, 0xfa, 0xa2, 0x24, 0x63, 0xbf, 0xf1, 0x20, 0x24, 0xea, 0x98, 0x02, 0x9f, 0x16, 0xed, 0x72, - 0xcc, 0xa9, 0xa9, 0x1d, 0xb4, 0x95, 0xa5, 0x49, 0x53, 0x12, 0xc5, 0xa6, 0x1a, 0xd3, 0x75, 0x9b, - 0x10, 0x11, 0x45, 0xb1, 0xc6, 0x84, 0xb8, 0x8e, 0x3e, 0x90, 0x39, 0x29, 0xa1, 0xae, 0x46, 0x4d, - 0x48, 0x53, 0x50, 0xd1, 0x13, 0xb8, 0x6a, 0x0f, 0xad, 0x1e, 0xf1, 0x34, 0xe7, 0xc0, 0x07, 0xca, - 0xc8, 0x63, 0x1c, 0x7b, 0x5c, 0xf3, 0x88, 0x4e, 0xe8, 0x48, 0x9c, 0xb8, 0x6f, 0x39, 0x93, 0x7d, - 0x51, 0x42, 0xbd, 0xec, 0x8b, 0x3c, 0x3a, 0x90, 0x3a, 0x58, 0xd7, 0xe9, 0x08, 0xb8, 0x1a, 0xa2, - 0x7d, 0xc3, 0x18, 0x6a, 0xc1, 0x25, 0x0b, 0x3f, 0xd7, 0x22, 0x67, 0x16, 0x86, 0x13, 0x9b, 0x0d, - 0x99, 0x36, 0x49, 0xe6, 0x41, 0x6f, 0x94, 0xb7, 0xf0, 0xf3, 0x76, 0x80, 0xab, 0x87, 0xb0, 0xfd, - 0x08, 0x75, 0x2f, 0x99, 0x4a, 0x66, 0x17, 0xee, 0x25, 0x53, 0x0b, 0xd9, 0xc5, 0x7b, 0xc9, 0x54, - 0x2a, 0x9b, 0x2e, 0xfd, 0x3f, 0xa4, 0x65, 0x5c, 0xd7, 0xf4, 0x43, 0x26, 0xb3, 0xbb, 0x61, 0x78, - 0x84, 0x31, 0xc2, 0x72, 0x4a, 0x90, 0xdd, 0x43, 0x42, 0x89, 0xc3, 0xe6, 0x69, 0x37, 0x06, 0x86, - 0x3e, 0x85, 0x25, 0x97, 0xc8, 0x76, 0x56, 0x0a, 0x66, 0x76, 0x3f, 0xa9, 0xcc, 0x70, 0xd5, 0xab, - 0x9c, 0xa6, 0x50, 0x0d, 0xb5, 0x95, 0xbc, 0xc9, 0x3d, 0xe5, 0x48, 0xaf, 0xc0, 0xd0, 0xfe, 0xd1, - 0x49, 0xbf, 0xff, 0x5a, 0x93, 0x1e, 0xd1, 0x37, 0x99, 0xf3, 0x2a, 0x64, 0x6a, 0xfe, 0xb2, 0x1f, - 0x88, 0xd2, 0x75, 0x6c, 0x5b, 0x96, 0xe3, 0xdb, 0xb2, 0x07, 0xab, 0x41, 0xf3, 0xd7, 0x75, 0x64, - 0x6e, 0x42, 0xef, 0x03, 0x04, 0x5d, 0xa3, 0xc8, 0x69, 0x7e, 0x76, 0x4f, 0x07, 0x94, 0x96, 0x31, - 0x55, 0xd1, 0xe7, 0xa7, 0x2a, 0xba, 0xac, 0x1a, 0x0e, 0x6c, 0xee, 0xc7, 0xab, 0xae, 0x2c, 0x20, - 0x6d, 0xac, 0x1f, 0x12, 0xce, 0x90, 0x0a, 0x49, 0x59, 0x5d, 0xfd, 0xe5, 0x5e, 0x3f, 0x75, 0xb9, - 0xa3, 0x9d, 0xca, 0x69, 0x4a, 0x1a, 0x98, 0xe3, 0x20, 0x06, 0xa4, 0xae, 0xd2, 0xcf, 0x15, 0xc8, - 0xdd, 0x27, 0xe3, 0x1a, 0x63, 0xb4, 0x6f, 0x5b, 0xc4, 0xe6, 0x22, 0xfa, 0xb0, 0x4e, 0xc4, 0x27, - 0xfa, 0x00, 0x56, 0x22, 0xc7, 0x93, 0xc9, 0x53, 0x91, 0xc9, 0x73, 0x39, 0x24, 0x8a, 0x7d, 0x42, - 0x37, 0x00, 0x5c, 0x8f, 0x8c, 0x34, 0x5d, 0x3b, 0x24, 0x63, 0xb9, 0xa6, 0xcc, 0xee, 0x76, 0x3c, - 0x29, 0xfa, 0xb7, 0xde, 0x4a, 0x7b, 0xd8, 0x33, 0xa9, 0x7e, 0x9f, 0x8c, 0xd5, 0x94, 0xc0, 0xd7, - 0xef, 0x93, 0xb1, 0xa8, 0x82, 0xb2, 0x49, 0x91, 0x99, 0x2c, 0xa1, 0xfa, 0x83, 0xd2, 0x2f, 0x15, - 0xb8, 0x10, 0x2d, 0x20, 0x3c, 0xaf, 0xf6, 0xb0, 0x27, 0x24, 0xe2, 0xfb, 0xa7, 0x4c, 0x77, 0x44, - 0xc7, 0xac, 0x9d, 0x3f, 0xc1, 0xda, 0x9b, 0xb0, 0x1c, 0xa5, 0x12, 0x61, 0x6f, 0x62, 0x06, 0x7b, - 0x33, 0xa1, 0xc4, 0x7d, 0x32, 0x2e, 0xfd, 0x24, 0x66, 0xdb, 0xad, 0x71, 0xcc, 0x85, 0xbd, 0x57, - 0xd8, 0x16, 0x4d, 0x1b, 0xb7, 0x4d, 0x8f, 0xcb, 0x1f, 0x5b, 0x40, 0xe2, 0xf8, 0x02, 0x4a, 0x7f, - 0x51, 0x60, 0x23, 0x3e, 0x2b, 0xeb, 0x3a, 0x6d, 0x6f, 0x68, 0x93, 0xfd, 0xdd, 0xb3, 0xe6, 0xbf, - 0x09, 0x29, 0x57, 0xa0, 0x34, 0xce, 0x82, 0x23, 0x9a, 0xad, 0x64, 0x2f, 0x49, 0xa9, 0xae, 0x08, - 0xf1, 0xd5, 0xa9, 0x05, 0xb0, 0x60, 0xe7, 0x3e, 0x9a, 0x29, 0xe8, 0x62, 0x01, 0xa5, 0xae, 0xc4, - 0xd7, 0xcc, 0x4a, 0x7f, 0x50, 0x00, 0x1d, 0xcf, 0x56, 0xe8, 0x3b, 0x80, 0xa6, 0x72, 0x5e, 0xdc, - 0xff, 0xb2, 0x6e, 0x2c, 0xcb, 0xc9, 0x9d, 0x8b, 0xfc, 0x68, 0x3e, 0xe6, 0x47, 0xe8, 0x7b, 0x00, - 0xae, 0x3c, 0xc4, 0x99, 0x4f, 0x3a, 0xed, 0x86, 0x9f, 0xa8, 0x00, 0x99, 0x1f, 0x39, 0xd4, 0x8e, - 0x3f, 0x58, 0x24, 0x54, 0x10, 0x24, 0xff, 0x2d, 0xa2, 0xf4, 0x33, 0x65, 0x92, 0x12, 0x83, 0x6c, - 0x5d, 0x33, 0xcd, 0xa0, 0x07, 0x44, 0x2e, 0x2c, 0x85, 0xf9, 0xde, 0x0f, 0xd7, 0xed, 0x13, 0x6b, - 0x52, 0x83, 0xe8, 0xb2, 0x2c, 0x5d, 0x17, 0x3b, 0xfe, 0xdb, 0x6f, 0x0b, 0x57, 0xfb, 0x94, 0x0f, - 0x86, 0xbd, 0x8a, 0xee, 0x58, 0xc1, 0x2b, 0x4e, 0xf0, 0xef, 0x1a, 0x33, 0x0e, 0xab, 0x7c, 0xec, - 0x12, 0x16, 0xca, 0xb0, 0xdf, 0xfc, 0xe3, 0x77, 0x57, 0x14, 0x35, 0x9c, 0xa6, 0x64, 0x40, 0x36, - 0xba, 0x83, 0x10, 0x8e, 0x0d, 0xcc, 0x31, 0x42, 0x90, 0xb4, 0xb1, 0x15, 0x36, 0x99, 0xf2, 0x7b, - 0x86, 0x1e, 0x73, 0x0b, 0x52, 0x56, 0xa0, 0x21, 0xb8, 0x75, 0x44, 0xe3, 0xd2, 0x4f, 0x17, 0xa1, - 0x18, 0x4e, 0xd3, 0xf2, 0xdf, 0x66, 0xe8, 0x8f, 0xfd, 0x16, 0x5c, 0x74, 0x4e, 0xa2, 0x7e, 0xb3, - 0x13, 0xde, 0x7b, 0x94, 0xb7, 0xf3, 0xde, 0x33, 0xff, 0xca, 0xf7, 0x9e, 0xc4, 0x2b, 0xde, 0x7b, - 0x92, 0x6f, 0xef, 0xbd, 0x67, 0xe1, 0xad, 0xbf, 0xf7, 0x2c, 0xbe, 0xa3, 0xf7, 0x9e, 0xa5, 0xff, - 0xc9, 0x7b, 0x4f, 0xea, 0xad, 0xbe, 0xf7, 0xa4, 0xdf, 0xec, 0xbd, 0x07, 0xde, 0xe8, 0xbd, 0x27, - 0x33, 0xd3, 0x7b, 0x4f, 0xe9, 0x17, 0xf3, 0xb0, 0x21, 0x6f, 0xd2, 0x9d, 0x01, 0x76, 0xc5, 0xe1, - 0x4e, 0x42, 0x20, 0xba, 0x9e, 0x2b, 0x33, 0x5c, 0xcf, 0xe7, 0x5f, 0xef, 0x7a, 0x9e, 0x98, 0xe1, - 0x7a, 0x9e, 0x3c, 0xeb, 0x7a, 0xbe, 0x70, 0xd6, 0xf5, 0x7c, 0x71, 0xb6, 0xeb, 0xf9, 0xd2, 0x29, - 0xd7, 0xf3, 0x52, 0x01, 0x32, 0x51, 0x82, 0x30, 0x18, 0xca, 0x42, 0x82, 0x1a, 0x61, 0x43, 0x29, - 0x3e, 0x4b, 0x3b, 0x70, 0xa1, 0x16, 0x9a, 0x45, 0x8c, 0xf8, 0xed, 0x18, 0x6d, 0xc0, 0xa2, 0x7f, - 0x43, 0x0d, 0xf0, 0xc1, 0xe8, 0xca, 0x9f, 0x14, 0x58, 0x89, 0x1a, 0x81, 0x01, 0x66, 0x04, 0xe5, - 0x61, 0xab, 0xfe, 0x68, 0xaf, 0xf3, 0xf8, 0x61, 0x53, 0xd5, 0xda, 0x77, 0x6b, 0x9d, 0xa6, 0xf6, - 0x78, 0xaf, 0xd3, 0x6e, 0xd6, 0x5b, 0xb7, 0x5b, 0xcd, 0x46, 0x76, 0x0e, 0xbd, 0x0f, 0x9b, 0x47, - 0xf8, 0x6a, 0xf3, 0x4e, 0xab, 0xd3, 0x6d, 0xaa, 0xcd, 0x46, 0x56, 0x39, 0x41, 0xbc, 0xb5, 0xd7, - 0xea, 0xb6, 0x6a, 0x0f, 0x5a, 0x4f, 0x9a, 0x8d, 0xec, 0x3c, 0xba, 0x08, 0x17, 0x8e, 0xf0, 0x1f, - 0xd4, 0x1e, 0xef, 0xd5, 0xef, 0x36, 0x1b, 0xd9, 0x04, 0xda, 0x82, 0x8d, 0x23, 0xcc, 0x4e, 0xf7, - 0x51, 0xbb, 0xdd, 0x6c, 0x64, 0x93, 0x27, 0xf0, 0x1a, 0xcd, 0x07, 0xcd, 0x6e, 0xb3, 0x91, 0x5d, - 0xd8, 0x4a, 0x7e, 0xf6, 0xeb, 0xfc, 0xdc, 0xad, 0x4f, 0xbf, 0x7a, 0x91, 0x57, 0xbe, 0x7e, 0x91, - 0x57, 0xfe, 0xfe, 0x22, 0xaf, 0x7c, 0xfe, 0x32, 0x3f, 0xf7, 0xf5, 0xcb, 0xfc, 0xdc, 0xdf, 0x5e, - 0xe6, 0xe7, 0x9e, 0x7c, 0x72, 0x3c, 0xf9, 0x4f, 0x8a, 0xeb, 0xb5, 0xe8, 0xc7, 0x90, 0xd1, 0x77, - 0xab, 0xcf, 0xa7, 0x7f, 0x6a, 0x91, 0x75, 0xa1, 0xb7, 0x28, 0xe3, 0xfa, 0xe3, 0xff, 0x06, 0x00, - 0x00, 0xff, 0xff, 0x21, 0xa7, 0x53, 0x56, 0x9b, 0x19, 0x00, 0x00, + // 2280 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x59, 0xcd, 0x6f, 0x1b, 0xc7, + 0xd9, 0xd7, 0x8a, 0x94, 0x44, 0x0e, 0xf5, 0x41, 0x8d, 0x1d, 0x99, 0x92, 0x15, 0x92, 0xde, 0xbc, + 0x0e, 0xf8, 0xda, 0x35, 0x19, 0x29, 0x40, 0x61, 0xb8, 0x0d, 0x0c, 0x9a, 0xa4, 0x6d, 0xfa, 0x43, + 0x66, 0x97, 0xb4, 0x02, 0xb8, 0x87, 0xc5, 0x70, 0x77, 0x44, 0x4e, 0xb5, 0xbb, 0xb3, 0xde, 0x19, + 0xae, 0xcd, 0x1e, 0x7a, 0x0e, 0x0a, 0x14, 0x48, 0x6f, 0x41, 0x2f, 0x0d, 0xd0, 0x4b, 0xd1, 0x53, + 0x0f, 0x45, 0xfe, 0x80, 0x9e, 0xd2, 0x02, 0x05, 0xd2, 0x5b, 0x4f, 0x49, 0x61, 0x1f, 0x7a, 0x28, + 0xd0, 0x9e, 0x7b, 0x2b, 0x66, 0xf6, 0x83, 0x4b, 0x7d, 0x99, 0x86, 0xed, 0x5e, 0xa4, 0x9d, 0x79, + 0x7e, 0xcf, 0x33, 0xcf, 0x33, 0xf3, 0x7c, 0xcd, 0x10, 0xec, 0x12, 0x87, 0x63, 0xcf, 0x18, 0x22, + 0xe2, 0xe8, 0x0c, 0x1b, 0x23, 0x8f, 0xf0, 0x71, 0xcd, 0x30, 0xfc, 0x9a, 0xeb, 0x51, 0x9f, 0x98, + 0xd8, 0xab, 0xf9, 0x3b, 0xf1, 0x77, 0xd5, 0xf5, 0x28, 0xa7, 0xf0, 0x83, 0x13, 0x78, 0xaa, 0x86, + 0xe1, 0x57, 0x63, 0x9c, 0xbf, 0xb3, 0xb5, 0x8e, 0x6c, 0xe2, 0xd0, 0x9a, 0xfc, 0x1b, 0xf0, 0x6d, + 0x15, 0x0d, 0xca, 0x6c, 0xca, 0x6a, 0x7d, 0xc4, 0x70, 0xcd, 0xdf, 0xe9, 0x63, 0x8e, 0x76, 0x6a, + 0x06, 0x25, 0x4e, 0x48, 0xff, 0x30, 0xa4, 0x63, 0x21, 0xc4, 0x31, 0x26, 0x98, 0x68, 0x22, 0xc4, + 0x6d, 0x06, 0x38, 0x5d, 0x8e, 0x6a, 0xc1, 0x20, 0x24, 0x9d, 0x1f, 0xd0, 0x01, 0x0d, 0xe6, 0xc5, + 0x57, 0xb4, 0xf0, 0x80, 0xd2, 0x81, 0x85, 0x6b, 0x72, 0xd4, 0x1f, 0x1d, 0xd4, 0xcc, 0x91, 0x87, + 0x38, 0xa1, 0xd1, 0xc2, 0xa5, 0xa3, 0x74, 0x4e, 0x6c, 0xcc, 0x38, 0xb2, 0xdd, 0x08, 0x40, 0xfa, + 0x46, 0xcd, 0xa0, 0x1e, 0xae, 0x19, 0x16, 0xc1, 0x0e, 0x17, 0x9b, 0x12, 0x7c, 0x85, 0x80, 0x9a, + 0x00, 0x58, 0x64, 0x30, 0xe4, 0xc1, 0x34, 0xab, 0x71, 0xec, 0x98, 0xd8, 0xb3, 0x49, 0x00, 0x9e, + 0x8c, 0x42, 0x86, 0xcb, 0xa7, 0xed, 0xbb, 0xbf, 0x53, 0x7b, 0x46, 0xbc, 0xc8, 0xd4, 0xed, 0x84, + 0x18, 0xc3, 0x1b, 0xbb, 0x9c, 0xd6, 0x0e, 0xf1, 0x38, 0xb4, 0x56, 0xfd, 0x4f, 0x06, 0x14, 0x1a, + 0xd4, 0x61, 0x23, 0x1b, 0x7b, 0x75, 0xd3, 0x24, 0xc2, 0xa4, 0x8e, 0x47, 0x5d, 0xca, 0x90, 0x05, + 0xcf, 0x83, 0x05, 0x4e, 0xb8, 0x85, 0x0b, 0x4a, 0x59, 0xa9, 0x64, 0xb5, 0x60, 0x00, 0xcb, 0x20, + 0x67, 0x62, 0x66, 0x78, 0xc4, 0x15, 0xe0, 0xc2, 0xbc, 0xa4, 0x25, 0xa7, 0xe0, 0x26, 0xc8, 0x04, + 0x6a, 0x11, 0xb3, 0x90, 0x92, 0xe4, 0x25, 0x39, 0x6e, 0x9b, 0xf0, 0x0e, 0x58, 0x25, 0x0e, 0xe1, + 0x04, 0x59, 0xfa, 0x10, 0x0b, 0x63, 0x0b, 0xe9, 0xb2, 0x52, 0xc9, 0xed, 0x6e, 0x55, 0x49, 0xdf, + 0xa8, 0x8a, 0xfd, 0xa9, 0x86, 0xbb, 0xe2, 0xef, 0x54, 0xef, 0x4a, 0xc4, 0xad, 0xf4, 0xd7, 0xdf, + 0x96, 0xe6, 0xb4, 0x95, 0x90, 0x2f, 0x98, 0x84, 0x97, 0xc0, 0xf2, 0x00, 0x3b, 0x98, 0x11, 0xa6, + 0x0f, 0x11, 0x1b, 0x16, 0x16, 0xca, 0x4a, 0x65, 0x59, 0xcb, 0x85, 0x73, 0x77, 0x11, 0x1b, 0xc2, + 0x12, 0xc8, 0xf5, 0x89, 0x83, 0xbc, 0x71, 0x80, 0x58, 0x94, 0x08, 0x10, 0x4c, 0x49, 0x40, 0x03, + 0x00, 0xe6, 0xa2, 0x67, 0x8e, 0x2e, 0x0e, 0xab, 0xb0, 0x14, 0x2a, 0x12, 0x9c, 0x64, 0x35, 0x3a, + 0xc9, 0x6a, 0x2f, 0x3a, 0xc9, 0x5b, 0x19, 0xa1, 0xc8, 0xe7, 0xdf, 0x95, 0x14, 0x2d, 0x2b, 0xf9, + 0x04, 0x05, 0xee, 0x81, 0xfc, 0xc8, 0xe9, 0x53, 0xc7, 0x24, 0xce, 0x40, 0x77, 0xb1, 0x47, 0xa8, + 0x59, 0xc8, 0x48, 0x51, 0x9b, 0xc7, 0x44, 0x35, 0x43, 0xa7, 0x09, 0x24, 0x7d, 0x21, 0x24, 0xad, + 0xc5, 0xcc, 0x1d, 0xc9, 0x0b, 0x7f, 0x04, 0xa0, 0x61, 0xf8, 0x52, 0x25, 0x3a, 0xe2, 0x91, 0xc4, + 0xec, 0xec, 0x12, 0xf3, 0x86, 0xe1, 0xf7, 0x02, 0xee, 0x50, 0xe4, 0x8f, 0xc1, 0x05, 0xee, 0x21, + 0x87, 0x1d, 0x60, 0xef, 0xa8, 0x5c, 0x30, 0xbb, 0xdc, 0xf7, 0x22, 0x19, 0xd3, 0xc2, 0xef, 0x82, + 0xb2, 0x11, 0x3a, 0x90, 0xee, 0x61, 0x93, 0x30, 0xee, 0x91, 0xfe, 0x48, 0xf0, 0xea, 0x07, 0x1e, + 0x32, 0xa4, 0x8f, 0xe4, 0xa4, 0x13, 0x14, 0x23, 0x9c, 0x36, 0x05, 0xbb, 0x1d, 0xa2, 0xe0, 0x23, + 0xf0, 0x7f, 0x7d, 0x8b, 0x1a, 0x87, 0x4c, 0x28, 0xa7, 0x4f, 0x49, 0x92, 0x4b, 0xdb, 0x84, 0x31, + 0x21, 0x6d, 0xb9, 0xac, 0x54, 0x52, 0xda, 0xa5, 0x00, 0xdb, 0xc1, 0x5e, 0x33, 0x81, 0xec, 0x25, + 0x80, 0xf0, 0x1a, 0x80, 0x43, 0xc2, 0x38, 0xf5, 0x88, 0x81, 0x2c, 0x1d, 0x3b, 0xdc, 0x23, 0x98, + 0x15, 0x56, 0x24, 0xfb, 0xfa, 0x84, 0xd2, 0x0a, 0x08, 0xf0, 0x1e, 0xb8, 0x74, 0xea, 0xa2, 0xba, + 0x31, 0x44, 0x8e, 0x83, 0xad, 0xc2, 0xaa, 0x34, 0xa5, 0x64, 0x9e, 0xb2, 0x66, 0x23, 0x80, 0xc1, + 0x73, 0x60, 0x81, 0x53, 0x57, 0xdf, 0x2b, 0xac, 0x95, 0x95, 0xca, 0x8a, 0x96, 0xe6, 0xd4, 0xdd, + 0x83, 0x1f, 0x81, 0xf3, 0x3e, 0xb2, 0x88, 0x89, 0x38, 0xf5, 0x98, 0xee, 0xd2, 0x67, 0xd8, 0xd3, + 0x0d, 0xe4, 0x16, 0xf2, 0x12, 0x03, 0x27, 0xb4, 0x8e, 0x20, 0x35, 0x90, 0x0b, 0xaf, 0x80, 0xf5, + 0x78, 0x56, 0x67, 0x98, 0x4b, 0xf8, 0xba, 0x84, 0xaf, 0xc5, 0x84, 0x2e, 0xe6, 0x02, 0xbb, 0x0d, + 0xb2, 0xc8, 0xb2, 0xe8, 0x33, 0x8b, 0x30, 0x5e, 0x80, 0xe5, 0x54, 0x25, 0xab, 0x4d, 0x26, 0xe0, + 0x16, 0xc8, 0x98, 0xd8, 0x19, 0x4b, 0xe2, 0x39, 0x49, 0x8c, 0xc7, 0xf0, 0x22, 0xc8, 0xda, 0x22, + 0x89, 0x70, 0x74, 0x88, 0x0b, 0xe7, 0xcb, 0x4a, 0x25, 0xad, 0x65, 0x6c, 0xe2, 0x74, 0xc5, 0x18, + 0x56, 0xc1, 0x39, 0x29, 0x45, 0x27, 0x8e, 0x38, 0x27, 0x1f, 0xeb, 0x3e, 0xb2, 0x58, 0xe1, 0xbd, + 0xb2, 0x52, 0xc9, 0x68, 0xeb, 0x92, 0xd4, 0x0e, 0x29, 0xfb, 0xc8, 0x62, 0x37, 0x2a, 0x9f, 0x7d, + 0x59, 0x9a, 0xfb, 0xe2, 0xcb, 0xd2, 0xdc, 0x9f, 0xff, 0x70, 0x6d, 0x2b, 0xcc, 0xac, 0x03, 0xea, + 0x57, 0xc3, 0x4c, 0x5c, 0x6d, 0x50, 0x87, 0x63, 0x87, 0x17, 0x14, 0xf5, 0xaf, 0x0a, 0xb8, 0xd0, + 0x88, 0x5d, 0xc2, 0xa6, 0x3e, 0xb2, 0xde, 0x65, 0xea, 0xa9, 0x83, 0x2c, 0x13, 0x67, 0x22, 0x83, + 0x3d, 0xfd, 0x1a, 0xc1, 0x9e, 0x11, 0x6c, 0x82, 0x70, 0xa3, 0xfc, 0x4a, 0x9b, 0xfe, 0x3d, 0x0f, + 0xb6, 0x23, 0x9b, 0x1e, 0x52, 0x93, 0x1c, 0x10, 0x03, 0xbd, 0xeb, 0x9c, 0x1a, 0xfb, 0x5a, 0x7a, + 0x06, 0x5f, 0x5b, 0x78, 0x3d, 0x5f, 0x5b, 0x9c, 0xc1, 0xd7, 0x96, 0xce, 0xf2, 0xb5, 0xcc, 0x59, + 0xbe, 0x96, 0x9d, 0xcd, 0xd7, 0xc0, 0x69, 0xbe, 0x36, 0x5f, 0x50, 0xd4, 0x5f, 0x2b, 0xe0, 0x7c, + 0xeb, 0xe9, 0x88, 0xf8, 0xf4, 0x2d, 0xed, 0xf4, 0x7d, 0xb0, 0x82, 0x13, 0xf2, 0x58, 0x21, 0x55, + 0x4e, 0x55, 0x72, 0xbb, 0x97, 0xab, 0xe1, 0xc1, 0xc7, 0xad, 0x44, 0x74, 0xfa, 0xc9, 0xd5, 0xb5, + 0x69, 0x5e, 0xa9, 0xe1, 0x1f, 0x15, 0xb0, 0x25, 0xf2, 0xc2, 0x00, 0x6b, 0xf8, 0x19, 0xf2, 0xcc, + 0x26, 0x76, 0xa8, 0xcd, 0xde, 0x58, 0x4f, 0x15, 0xac, 0x98, 0x52, 0x92, 0xce, 0xa9, 0x8e, 0x4c, + 0x53, 0xea, 0x29, 0x31, 0x62, 0xb2, 0x47, 0xeb, 0xa6, 0x09, 0x2b, 0x20, 0x3f, 0xc1, 0x78, 0x22, + 0xc6, 0x84, 0xeb, 0x0b, 0xd8, 0x6a, 0x04, 0x93, 0x91, 0x87, 0x6f, 0x14, 0xcf, 0x76, 0x6d, 0xf5, + 0x9f, 0x0a, 0xc8, 0xdf, 0xb1, 0x68, 0x1f, 0x59, 0x5d, 0x0b, 0xb1, 0xa1, 0xc8, 0x99, 0x63, 0x11, + 0x52, 0x1e, 0x0e, 0x8b, 0x95, 0x54, 0x7f, 0xe6, 0x90, 0x12, 0x6c, 0xb2, 0x7c, 0xde, 0x04, 0xeb, + 0x71, 0xf9, 0x88, 0x1d, 0x5c, 0x5a, 0x7b, 0xeb, 0xdc, 0x8b, 0x6f, 0x4b, 0x6b, 0x51, 0x30, 0x35, + 0xa4, 0xb3, 0x37, 0xb5, 0x35, 0x63, 0x6a, 0xc2, 0x84, 0x45, 0x90, 0x23, 0x7d, 0x43, 0x67, 0xf8, + 0xa9, 0xee, 0x8c, 0x6c, 0x19, 0x1b, 0x69, 0x2d, 0x4b, 0xfa, 0x46, 0x17, 0x3f, 0xdd, 0x1b, 0xd9, + 0xf0, 0x63, 0xb0, 0x11, 0x35, 0x95, 0xc2, 0x9b, 0x74, 0xc1, 0x2f, 0xb6, 0xcb, 0x93, 0xe1, 0xb2, + 0xac, 0x9d, 0x8b, 0xa8, 0xfb, 0xc8, 0x12, 0x8b, 0xd5, 0x4d, 0xd3, 0x53, 0xff, 0xb5, 0x00, 0x16, + 0x3b, 0xc8, 0x43, 0x36, 0x83, 0x3d, 0xb0, 0xc6, 0xb1, 0xed, 0x5a, 0x88, 0x63, 0x3d, 0x68, 0x4d, + 0x42, 0x4b, 0xaf, 0xca, 0x96, 0x25, 0xd9, 0xb1, 0x55, 0x13, 0x3d, 0x9a, 0xbf, 0x53, 0x6d, 0xc8, + 0xd9, 0x2e, 0x47, 0x1c, 0x6b, 0xab, 0x91, 0x8c, 0x60, 0x12, 0x5e, 0x07, 0x05, 0xee, 0x8d, 0x18, + 0x9f, 0x34, 0x0d, 0x93, 0x6a, 0x19, 0x9c, 0xf5, 0x46, 0x44, 0x0f, 0xea, 0x6c, 0x5c, 0x25, 0x4f, + 0xee, 0x0f, 0x52, 0x6f, 0xd2, 0x1f, 0x98, 0x60, 0x9b, 0x89, 0x43, 0xd5, 0x6d, 0xcc, 0x65, 0x15, + 0x77, 0x2d, 0xec, 0x10, 0x36, 0x8c, 0x84, 0x2f, 0xce, 0x2e, 0x7c, 0x53, 0x0a, 0x7a, 0x28, 0xe4, + 0x68, 0x91, 0x98, 0x70, 0x95, 0x06, 0x28, 0x9e, 0xbc, 0x4a, 0x6c, 0xf8, 0x92, 0x34, 0xfc, 0xe2, + 0x09, 0x22, 0x62, 0xeb, 0x19, 0xf8, 0x30, 0xd1, 0x6d, 0x88, 0x68, 0xd2, 0xa5, 0x23, 0xeb, 0x1e, + 0x1e, 0x88, 0x92, 0x8c, 0x82, 0xc6, 0x03, 0xe3, 0xb8, 0x63, 0x0a, 0x7d, 0x5a, 0xdc, 0x18, 0x12, + 0x4e, 0x4d, 0x9c, 0xb0, 0xad, 0x54, 0x27, 0x4d, 0x49, 0x1c, 0x9b, 0x5a, 0x42, 0xd6, 0x6d, 0x8c, + 0x45, 0x14, 0x25, 0x1a, 0x13, 0xec, 0x52, 0x63, 0x28, 0x73, 0x52, 0x4a, 0x5b, 0x8d, 0x9b, 0x90, + 0x96, 0x98, 0x85, 0x4f, 0xc0, 0x55, 0x67, 0x64, 0xf7, 0xb1, 0xa7, 0xd3, 0x83, 0x00, 0x28, 0x23, + 0x8f, 0x71, 0xe4, 0x71, 0xdd, 0xc3, 0x06, 0x26, 0xbe, 0x38, 0xf1, 0x40, 0x73, 0x26, 0xfb, 0xa2, + 0x94, 0x76, 0x39, 0x60, 0x79, 0x74, 0x20, 0x65, 0xb0, 0x1e, 0xed, 0x0a, 0xb8, 0x16, 0xa1, 0x03, + 0xc5, 0x18, 0x6c, 0x83, 0x4b, 0x36, 0x7a, 0xae, 0xc7, 0xce, 0x2c, 0x14, 0xc7, 0x0e, 0x1b, 0x31, + 0x7d, 0x92, 0xcc, 0xc3, 0xde, 0xa8, 0x68, 0xa3, 0xe7, 0x9d, 0x10, 0xd7, 0x88, 0x60, 0xfb, 0x31, + 0xea, 0x5e, 0x3a, 0x93, 0xce, 0x2f, 0xdc, 0x4b, 0x67, 0x16, 0xf2, 0x8b, 0xf7, 0xd2, 0x99, 0x4c, + 0x3e, 0xab, 0xfe, 0x3f, 0xc8, 0xca, 0xb8, 0xae, 0x1b, 0x87, 0x4c, 0x66, 0x77, 0xd3, 0xf4, 0x30, + 0x63, 0x98, 0x15, 0x94, 0x30, 0xbb, 0x47, 0x13, 0x2a, 0x07, 0x9b, 0xa7, 0xdd, 0x18, 0x18, 0xfc, + 0x14, 0x2c, 0xb9, 0x58, 0xb6, 0xb3, 0x92, 0x31, 0xb7, 0xfb, 0x49, 0x75, 0x86, 0xab, 0x5e, 0xf5, + 0x34, 0x81, 0x5a, 0x24, 0x4d, 0xf5, 0x26, 0xf7, 0x94, 0x23, 0xbd, 0x02, 0x83, 0xfb, 0x47, 0x17, + 0xfd, 0xe1, 0x6b, 0x2d, 0x7a, 0x44, 0xde, 0x64, 0xcd, 0xab, 0x20, 0x57, 0x0f, 0xcc, 0x7e, 0x20, + 0x4a, 0xd7, 0xb1, 0x6d, 0x59, 0x4e, 0x6e, 0xcb, 0x1e, 0x58, 0x0d, 0x9b, 0xbf, 0x1e, 0x95, 0xb9, + 0x09, 0xbe, 0x0f, 0x40, 0xd8, 0x35, 0x8a, 0x9c, 0x16, 0x64, 0xf7, 0x6c, 0x38, 0xd3, 0x36, 0xa7, + 0x2a, 0xfa, 0xfc, 0x54, 0x45, 0x97, 0x55, 0x83, 0x82, 0xcd, 0xfd, 0x64, 0xd5, 0x95, 0x05, 0xa4, + 0x83, 0x8c, 0x43, 0xcc, 0x19, 0xd4, 0x40, 0x5a, 0x56, 0xd7, 0xc0, 0xdc, 0xeb, 0xa7, 0x9a, 0xeb, + 0xef, 0x54, 0x4f, 0x13, 0xd2, 0x44, 0x1c, 0x85, 0x31, 0x20, 0x65, 0xa9, 0xbf, 0x54, 0x40, 0xe1, + 0x3e, 0x1e, 0xd7, 0x19, 0x23, 0x03, 0xc7, 0xc6, 0x0e, 0x17, 0xd1, 0x87, 0x0c, 0x2c, 0x3e, 0xe1, + 0x07, 0x60, 0x25, 0x76, 0x3c, 0x99, 0x3c, 0x15, 0x99, 0x3c, 0x97, 0xa3, 0x49, 0xb1, 0x4f, 0xf0, + 0x06, 0x00, 0xae, 0x87, 0x7d, 0xdd, 0xd0, 0x0f, 0xf1, 0x58, 0xda, 0x94, 0xdb, 0xdd, 0x4e, 0x26, + 0xc5, 0xe0, 0xfe, 0x59, 0xed, 0x8c, 0xfa, 0x16, 0x31, 0xee, 0xe3, 0xb1, 0x96, 0x11, 0xf8, 0xc6, + 0x7d, 0x3c, 0x16, 0x55, 0x50, 0x36, 0x29, 0x32, 0x93, 0xa5, 0xb4, 0x60, 0xa0, 0xfe, 0x4a, 0x01, + 0x17, 0x62, 0x03, 0xa2, 0xf3, 0xea, 0x8c, 0xfa, 0x82, 0x23, 0xb9, 0x7f, 0xca, 0x74, 0x47, 0x74, + 0x4c, 0xdb, 0xf9, 0x13, 0xb4, 0xbd, 0x09, 0x96, 0xe3, 0x54, 0x22, 0xf4, 0x4d, 0xcd, 0xa0, 0x6f, + 0x2e, 0xe2, 0xb8, 0x8f, 0xc7, 0xea, 0xcf, 0x12, 0xba, 0xdd, 0x1a, 0x27, 0x5c, 0xd8, 0x7b, 0x85, + 0x6e, 0xf1, 0xb2, 0x49, 0xdd, 0x8c, 0x24, 0xff, 0x31, 0x03, 0x52, 0xc7, 0x0d, 0x50, 0xff, 0xa2, + 0x80, 0x8d, 0xe4, 0xaa, 0xac, 0x47, 0x3b, 0xde, 0xc8, 0xc1, 0xfb, 0xbb, 0x67, 0xad, 0x7f, 0x13, + 0x64, 0x5c, 0x81, 0xd2, 0x39, 0x0b, 0x8f, 0x68, 0xb6, 0x92, 0xbd, 0x24, 0xb9, 0x7a, 0x22, 0xc4, + 0x57, 0xa7, 0x0c, 0x60, 0xe1, 0xce, 0x7d, 0x34, 0x53, 0xd0, 0x25, 0x02, 0x4a, 0x5b, 0x49, 0xda, + 0xcc, 0xd4, 0xaf, 0x14, 0x00, 0x8f, 0x67, 0x2b, 0xf8, 0x3d, 0x00, 0xa7, 0x72, 0x5e, 0xd2, 0xff, + 0xf2, 0x6e, 0x22, 0xcb, 0xc9, 0x9d, 0x8b, 0xfd, 0x68, 0x3e, 0xe1, 0x47, 0xf0, 0x07, 0x00, 0xb8, + 0xf2, 0x10, 0x67, 0x3e, 0xe9, 0xac, 0x1b, 0x7d, 0xc2, 0x12, 0xc8, 0xfd, 0x84, 0x12, 0x27, 0xf9, + 0x60, 0x91, 0xd2, 0x80, 0x98, 0x0a, 0xde, 0x22, 0xd4, 0x5f, 0x28, 0x93, 0x94, 0x18, 0x66, 0xeb, + 0xba, 0x65, 0x85, 0x3d, 0x20, 0x74, 0xc1, 0x52, 0x94, 0xef, 0x83, 0x70, 0xdd, 0x3e, 0xb1, 0x26, + 0x35, 0xb1, 0x21, 0xcb, 0xd2, 0x75, 0xb1, 0xe3, 0xbf, 0xfb, 0xae, 0x74, 0x75, 0x40, 0xf8, 0x70, + 0xd4, 0xaf, 0x1a, 0xd4, 0x0e, 0x1f, 0xa8, 0xc2, 0x7f, 0xd7, 0x98, 0x79, 0x58, 0xe3, 0x63, 0x17, + 0xb3, 0x88, 0x87, 0xfd, 0xf6, 0x1f, 0xbf, 0xbf, 0xa2, 0x68, 0xd1, 0x32, 0xaa, 0x09, 0xf2, 0xf1, + 0x1d, 0x04, 0x73, 0x64, 0x22, 0x8e, 0x20, 0x04, 0x69, 0x07, 0xd9, 0x51, 0x93, 0x29, 0xbf, 0x67, + 0xe8, 0x31, 0xb7, 0x40, 0xc6, 0x0e, 0x25, 0x84, 0xb7, 0x8e, 0x78, 0xac, 0xfe, 0x7c, 0x11, 0x94, + 0xa3, 0x65, 0xda, 0xc1, 0xdb, 0x0c, 0xf9, 0x69, 0xd0, 0x82, 0x8b, 0xce, 0x49, 0xd4, 0x6f, 0x76, + 0xc2, 0x7b, 0x8f, 0xf2, 0x76, 0xde, 0x7b, 0xe6, 0x5f, 0xf9, 0xde, 0x93, 0x7a, 0xc5, 0x7b, 0x4f, + 0xfa, 0xed, 0xbd, 0xf7, 0x2c, 0xbc, 0xf5, 0xf7, 0x9e, 0xc5, 0x77, 0xf4, 0xde, 0xb3, 0xf4, 0x3f, + 0x79, 0xef, 0xc9, 0xbc, 0xd5, 0xf7, 0x9e, 0xec, 0x9b, 0xbd, 0xf7, 0x80, 0x37, 0x7a, 0xef, 0xc9, + 0xcd, 0xf4, 0xde, 0xa3, 0x7e, 0x35, 0x0f, 0x36, 0xe4, 0x4d, 0xba, 0x3b, 0x44, 0xae, 0x38, 0xdc, + 0x49, 0x08, 0xc4, 0xd7, 0x73, 0x65, 0x86, 0xeb, 0xf9, 0xfc, 0xeb, 0x5d, 0xcf, 0x53, 0x33, 0x5c, + 0xcf, 0xd3, 0x67, 0x5d, 0xcf, 0x17, 0xce, 0xba, 0x9e, 0x2f, 0xce, 0x76, 0x3d, 0x5f, 0x3a, 0xe5, + 0x7a, 0x0e, 0x55, 0xb0, 0xec, 0x7a, 0x84, 0x8a, 0x3a, 0x90, 0x78, 0x0b, 0x98, 0x9a, 0x53, 0x4b, + 0x20, 0x17, 0x27, 0x11, 0x93, 0xc1, 0x3c, 0x48, 0x11, 0x33, 0x6a, 0x3a, 0xc5, 0xa7, 0xba, 0x03, + 0x2e, 0xd4, 0x23, 0xd5, 0xb1, 0x99, 0xbc, 0x41, 0xc3, 0x0d, 0xb0, 0x18, 0xdc, 0x62, 0x43, 0x7c, + 0x38, 0xba, 0xf2, 0x27, 0x05, 0xac, 0xc4, 0xcd, 0xc2, 0x10, 0x31, 0x0c, 0x8b, 0x60, 0xab, 0xf1, + 0x68, 0xaf, 0xfb, 0xf8, 0x61, 0x4b, 0xd3, 0x3b, 0x77, 0xeb, 0xdd, 0x96, 0xfe, 0x78, 0xaf, 0xdb, + 0x69, 0x35, 0xda, 0xb7, 0xdb, 0xad, 0x66, 0x7e, 0x0e, 0xbe, 0x0f, 0x36, 0x8f, 0xd0, 0xb5, 0xd6, + 0x9d, 0x76, 0xb7, 0xd7, 0xd2, 0x5a, 0xcd, 0xbc, 0x72, 0x02, 0x7b, 0x7b, 0xaf, 0xdd, 0x6b, 0xd7, + 0x1f, 0xb4, 0x9f, 0xb4, 0x9a, 0xf9, 0x79, 0x78, 0x11, 0x5c, 0x38, 0x42, 0x7f, 0x50, 0x7f, 0xbc, + 0xd7, 0xb8, 0xdb, 0x6a, 0xe6, 0x53, 0x70, 0x0b, 0x6c, 0x1c, 0x21, 0x76, 0x7b, 0x8f, 0x3a, 0x9d, + 0x56, 0x33, 0x9f, 0x3e, 0x81, 0xd6, 0x6c, 0x3d, 0x68, 0xf5, 0x5a, 0xcd, 0xfc, 0xc2, 0x56, 0xfa, + 0xb3, 0xdf, 0x14, 0xe7, 0x6e, 0x7d, 0xfa, 0xf5, 0x8b, 0xa2, 0xf2, 0xcd, 0x8b, 0xa2, 0xf2, 0xf7, + 0x17, 0x45, 0xe5, 0xf3, 0x97, 0xc5, 0xb9, 0x6f, 0x5e, 0x16, 0xe7, 0xfe, 0xf6, 0xb2, 0x38, 0xf7, + 0xe4, 0x93, 0xe3, 0x05, 0x62, 0x52, 0x80, 0xaf, 0xc5, 0xbf, 0x08, 0xf8, 0xdf, 0xaf, 0x3d, 0x9f, + 0xfe, 0x39, 0x46, 0xd6, 0x8e, 0xfe, 0xa2, 0x8c, 0xfd, 0x8f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, + 0x15, 0xc3, 0x5a, 0xbf, 0xbf, 0x19, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -3284,6 +3297,15 @@ func (m *PowerShapingParameters) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if len(m.Prioritylist) > 0 { + for iNdEx := len(m.Prioritylist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Prioritylist[iNdEx]) + copy(dAtA[i:], m.Prioritylist[iNdEx]) + i = encodeVarintProvider(dAtA, i, uint64(len(m.Prioritylist[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } if m.AllowInactiveVals { i-- if m.AllowInactiveVals { @@ -3977,6 +3999,12 @@ func (m *PowerShapingParameters) Size() (n int) { if m.AllowInactiveVals { n += 2 } + if len(m.Prioritylist) > 0 { + for _, s := range m.Prioritylist { + l = len(s) + n += 1 + l + sovProvider(uint64(l)) + } + } return n } @@ -8026,6 +8054,38 @@ func (m *PowerShapingParameters) Unmarshal(dAtA []byte) error { } } m.AllowInactiveVals = bool(v != 0) + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Prioritylist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Prioritylist = append(m.Prioritylist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index 578c12598e..f486a07b57 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -261,6 +261,11 @@ type Chain struct { ConsumerId string `protobuf:"bytes,13,opt,name=consumer_id,json=consumerId,proto3" json:"consumer_id,omitempty"` // the reward denoms allowlisted by this consumer chain AllowlistedRewardDenoms *AllowlistedRewardDenoms `protobuf:"bytes,14,opt,name=allowlisted_reward_denoms,json=allowlistedRewardDenoms,proto3" json:"allowlisted_reward_denoms,omitempty"` + // Corresponds to a list of provider consensus addresses of validators that should have PRIORITY to validate on the consumer chain, + // meaning as long as they are eligible/opted in to validate on the consumer chain, the validator set will be + // filled with these validators first, and other validators will be added to the validator set only if there are + // not enough eligible priority validators. + Prioritylist []string `protobuf:"bytes,15,rep,name=prioritylist,proto3" json:"prioritylist,omitempty"` } func (m *Chain) Reset() { *m = Chain{} } @@ -394,6 +399,13 @@ func (m *Chain) GetAllowlistedRewardDenoms() *AllowlistedRewardDenoms { return nil } +func (m *Chain) GetPrioritylist() []string { + if m != nil { + return m.Prioritylist + } + return nil +} + type QueryValidatorConsumerAddrRequest struct { // The consensus address of the validator on the provider chain ProviderAddress string `protobuf:"bytes,1,opt,name=provider_address,json=providerAddress,proto3" json:"provider_address,omitempty" yaml:"address"` @@ -1857,161 +1869,162 @@ func init() { } var fileDescriptor_422512d7b7586cd7 = []byte{ - // 2450 bytes of a gzipped FileDescriptorProto + // 2465 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x5a, 0xcd, 0x6f, 0xdc, 0xc6, 0x15, 0x17, 0x57, 0x1f, 0x5e, 0x8d, 0x2c, 0x39, 0x1e, 0xcb, 0xd6, 0x7a, 0x65, 0x6b, 0x65, 0x3a, 0x6e, 0x15, 0x39, 0x5e, 0x4a, 0x2a, 0xf2, 0xe5, 0xc4, 0x1f, 0x5a, 0x59, 0xb2, 0x05, 0xc7, 0xb6, 0x42, 0x29, 0x0e, 0xe0, 0xd4, 0x65, 0x47, 0xe4, 0x64, 0x35, 0x15, 0x97, 0xa4, 0x39, 0xa3, 0xb5, - 0xb7, 0x86, 0x2f, 0x3d, 0xe5, 0xd0, 0x02, 0x09, 0x8a, 0x9e, 0x9b, 0x73, 0x0f, 0x45, 0x51, 0x04, - 0xfd, 0x07, 0x7a, 0xc9, 0xad, 0x69, 0x7a, 0x29, 0x5a, 0xd4, 0x6d, 0xed, 0x16, 0xe8, 0xa5, 0x28, - 0x9a, 0xf6, 0x0f, 0x08, 0x66, 0x38, 0xe4, 0x2e, 0x69, 0x4a, 0x4b, 0x6a, 0x75, 0x13, 0x67, 0xde, - 0xfb, 0xbd, 0x8f, 0x79, 0xf3, 0xe6, 0xbd, 0xb7, 0x02, 0x1a, 0x71, 0x18, 0xf6, 0xcd, 0x2d, 0x44, - 0x1c, 0x83, 0x62, 0x73, 0xc7, 0x27, 0xac, 0xa5, 0x99, 0x66, 0x53, 0xf3, 0x7c, 0xb7, 0x49, 0x2c, - 0xec, 0x6b, 0xcd, 0x79, 0xed, 0xc1, 0x0e, 0xf6, 0x5b, 0x55, 0xcf, 0x77, 0x99, 0x0b, 0xcf, 0xa6, - 0x30, 0x54, 0x4d, 0xb3, 0x59, 0x0d, 0x19, 0xaa, 0xcd, 0xf9, 0xf2, 0xa9, 0xba, 0xeb, 0xd6, 0x6d, - 0xac, 0x21, 0x8f, 0x68, 0xc8, 0x71, 0x5c, 0x86, 0x18, 0x71, 0x1d, 0x1a, 0x40, 0x94, 0xc7, 0xeb, - 0x6e, 0xdd, 0x15, 0x7f, 0x6a, 0xfc, 0x2f, 0xb9, 0x5a, 0x91, 0x3c, 0xe2, 0x6b, 0x73, 0xe7, 0x23, - 0x8d, 0x91, 0x06, 0xa6, 0x0c, 0x35, 0x3c, 0x49, 0xb0, 0x90, 0x45, 0xd5, 0x48, 0x8b, 0x80, 0x67, - 0x6e, 0x37, 0x9e, 0xe6, 0xbc, 0x46, 0xb7, 0x90, 0x8f, 0x2d, 0xc3, 0x74, 0x1d, 0xba, 0xd3, 0x88, - 0x38, 0xce, 0xed, 0xc1, 0xf1, 0x90, 0xf8, 0x58, 0x92, 0x9d, 0x62, 0xd8, 0xb1, 0xb0, 0xdf, 0x20, - 0x0e, 0xd3, 0x4c, 0xbf, 0xe5, 0x31, 0x57, 0xdb, 0xc6, 0xad, 0xd0, 0xc2, 0x93, 0xa6, 0x4b, 0x1b, - 0x2e, 0x35, 0x02, 0x23, 0x83, 0x0f, 0xb9, 0xf5, 0x72, 0xf0, 0xa5, 0x51, 0x86, 0xb6, 0x89, 0x53, - 0xd7, 0x9a, 0xf3, 0x9b, 0x98, 0xa1, 0xf9, 0xf0, 0x5b, 0x52, 0xcd, 0x4a, 0xaa, 0x4d, 0x44, 0x71, - 0xe0, 0xfe, 0x88, 0xd0, 0x43, 0x75, 0xe2, 0x08, 0x7f, 0x06, 0xb4, 0xea, 0x65, 0x30, 0xf9, 0x1e, - 0xa7, 0x58, 0x92, 0x86, 0x5c, 0xc7, 0x0e, 0xa6, 0x84, 0xea, 0xf8, 0xc1, 0x0e, 0xa6, 0x0c, 0x56, - 0xc0, 0x48, 0x68, 0xa2, 0x41, 0xac, 0x92, 0x32, 0xad, 0xcc, 0x0c, 0xeb, 0x20, 0x5c, 0x5a, 0xb5, - 0xd4, 0xc7, 0xe0, 0x54, 0x3a, 0x3f, 0xf5, 0x5c, 0x87, 0x62, 0xf8, 0x21, 0x18, 0xad, 0x07, 0x4b, - 0x06, 0x65, 0x88, 0x61, 0x01, 0x31, 0xb2, 0x30, 0x57, 0xdd, 0x2d, 0x12, 0x9a, 0xf3, 0xd5, 0x04, - 0xd6, 0x3a, 0xe7, 0xab, 0x0d, 0x7c, 0xf1, 0xb4, 0xd2, 0xa7, 0x1f, 0xae, 0x77, 0xac, 0xa9, 0xbf, - 0x54, 0x40, 0x39, 0x26, 0x7d, 0x89, 0xe3, 0x45, 0xca, 0xdf, 0x00, 0x83, 0xde, 0x16, 0xa2, 0x81, - 0xcc, 0xb1, 0x85, 0x85, 0x6a, 0x86, 0xe8, 0x8b, 0x84, 0xaf, 0x71, 0x4e, 0x3d, 0x00, 0x80, 0x2b, - 0x00, 0xb4, 0x3d, 0x57, 0x2a, 0x08, 0x13, 0xbe, 0x55, 0x95, 0x47, 0xc3, 0xdd, 0x5c, 0x0d, 0xa2, - 0x5c, 0xba, 0xb9, 0xba, 0x86, 0xea, 0x58, 0x6a, 0xa1, 0x77, 0x70, 0xaa, 0xbf, 0x50, 0x12, 0xee, - 0x0e, 0x15, 0x96, 0xde, 0xaa, 0x81, 0x21, 0xa1, 0x1e, 0x2d, 0x29, 0xd3, 0xfd, 0x33, 0x23, 0x0b, - 0xb3, 0xd9, 0x54, 0xe6, 0xdb, 0xba, 0xe4, 0x84, 0xd7, 0x53, 0x74, 0xfd, 0x76, 0x57, 0x5d, 0x03, - 0x05, 0x62, 0xca, 0xfe, 0x67, 0x00, 0x0c, 0x0a, 0x68, 0x78, 0x12, 0x14, 0x03, 0x15, 0xa2, 0x10, - 0x38, 0x24, 0xbe, 0x57, 0x2d, 0x38, 0x09, 0x86, 0x4d, 0x9b, 0x60, 0x87, 0xf1, 0xbd, 0x82, 0xd8, - 0x2b, 0x06, 0x0b, 0xab, 0x16, 0x3c, 0x06, 0x06, 0x99, 0xeb, 0x19, 0xb7, 0x4b, 0xfd, 0xd3, 0xca, - 0xcc, 0xa8, 0x3e, 0xc0, 0x5c, 0xef, 0x36, 0x9c, 0x05, 0xb0, 0x41, 0x1c, 0xc3, 0x73, 0x1f, 0xf2, - 0x98, 0x72, 0x8c, 0x80, 0x62, 0x60, 0x5a, 0x99, 0xe9, 0xd7, 0xc7, 0x1a, 0xc4, 0x59, 0xe3, 0x1b, - 0xab, 0xce, 0x06, 0xa7, 0x9d, 0x03, 0xe3, 0x4d, 0x64, 0x13, 0x0b, 0x31, 0xd7, 0xa7, 0x92, 0xc5, - 0x44, 0x5e, 0x69, 0x50, 0xe0, 0xc1, 0xf6, 0x9e, 0x60, 0x5a, 0x42, 0x1e, 0x9c, 0x05, 0x47, 0xa3, - 0x55, 0x83, 0x62, 0x26, 0xc8, 0x87, 0x04, 0xf9, 0x91, 0x68, 0x63, 0x1d, 0x33, 0x4e, 0x7b, 0x0a, - 0x0c, 0x23, 0xdb, 0x76, 0x1f, 0xda, 0x84, 0xb2, 0xd2, 0xa1, 0xe9, 0xfe, 0x99, 0x61, 0xbd, 0xbd, - 0x00, 0xcb, 0xa0, 0x68, 0x61, 0xa7, 0x25, 0x36, 0x8b, 0x62, 0x33, 0xfa, 0x86, 0xe3, 0x61, 0x64, - 0x0d, 0x0b, 0x8b, 0x65, 0x94, 0x7c, 0x00, 0x8a, 0x0d, 0xcc, 0x90, 0x85, 0x18, 0x2a, 0x01, 0xe1, - 0xf7, 0xd7, 0x72, 0x85, 0xdc, 0x2d, 0xc9, 0x2c, 0x63, 0x3d, 0x02, 0xe3, 0x4e, 0xe6, 0x2e, 0xe3, - 0xb7, 0x1c, 0x97, 0x46, 0xa6, 0x95, 0x99, 0x01, 0xbd, 0xd8, 0x20, 0xce, 0x3a, 0xff, 0x86, 0x55, - 0x70, 0x4c, 0x28, 0x6d, 0x10, 0x07, 0x99, 0x8c, 0x34, 0xb1, 0xd1, 0x44, 0x36, 0x2d, 0x1d, 0x9e, - 0x56, 0x66, 0x8a, 0xfa, 0x51, 0xb1, 0xb5, 0x2a, 0x77, 0xee, 0x22, 0x9b, 0x26, 0xaf, 0xf4, 0x68, - 0xf2, 0x4a, 0xc3, 0x47, 0xe0, 0x64, 0xe4, 0x05, 0x6c, 0x19, 0x3e, 0x7e, 0x88, 0x7c, 0xcb, 0xb0, - 0xb0, 0xe3, 0x36, 0x68, 0x69, 0x4c, 0xd8, 0xf5, 0x4e, 0x26, 0xbb, 0x16, 0xdb, 0x28, 0xba, 0x00, - 0xb9, 0x26, 0x30, 0xf4, 0x09, 0x94, 0xbe, 0xa1, 0xfe, 0x44, 0x01, 0x67, 0xc4, 0xf5, 0xb8, 0x1b, - 0x9e, 0x54, 0xe8, 0x9a, 0x45, 0xcb, 0xf2, 0xc3, 0x6b, 0x7d, 0x09, 0xbc, 0x14, 0x4a, 0x31, 0x90, - 0x65, 0xf9, 0x98, 0xd2, 0x20, 0x2a, 0x6b, 0xf0, 0xeb, 0xa7, 0x95, 0xb1, 0x16, 0x6a, 0xd8, 0x17, - 0x55, 0xb9, 0xa1, 0xea, 0x47, 0x42, 0xda, 0xc5, 0x60, 0x25, 0x69, 0x7f, 0x21, 0x69, 0xff, 0xc5, - 0xe2, 0xc7, 0x9f, 0x55, 0xfa, 0xfe, 0xf5, 0x59, 0xa5, 0x4f, 0xbd, 0x03, 0xd4, 0xbd, 0xd4, 0x91, - 0x97, 0xf6, 0x15, 0xf0, 0x52, 0x04, 0x18, 0xd3, 0x47, 0x3f, 0x62, 0x76, 0xd0, 0x73, 0x6d, 0x5e, - 0x34, 0x70, 0xad, 0x43, 0xbb, 0x0e, 0x03, 0xd3, 0x01, 0xd3, 0x0d, 0x4c, 0x08, 0xe9, 0xc9, 0xc0, - 0xb8, 0x3a, 0x6d, 0x03, 0xd3, 0x1d, 0xfe, 0x82, 0x73, 0xd5, 0x49, 0x70, 0x52, 0x00, 0x6e, 0x6c, - 0xf9, 0x2e, 0x63, 0x36, 0x16, 0x79, 0x5a, 0xda, 0xa5, 0xfe, 0x3e, 0x4c, 0xd7, 0x89, 0x5d, 0x29, - 0xa6, 0x02, 0x46, 0xa8, 0x8d, 0xe8, 0x96, 0xd1, 0xc0, 0x0c, 0xfb, 0x42, 0x42, 0xbf, 0x0e, 0xc4, - 0xd2, 0x2d, 0xbe, 0x02, 0x17, 0xc0, 0xf1, 0x0e, 0x02, 0x43, 0x44, 0x11, 0x72, 0x4c, 0x2c, 0x4c, - 0xec, 0xd7, 0x8f, 0xb5, 0x49, 0x17, 0xc3, 0x2d, 0xf8, 0x3d, 0x50, 0x72, 0xf0, 0x23, 0x66, 0xf8, - 0xd8, 0xb3, 0xb1, 0x43, 0xe8, 0x96, 0x61, 0x22, 0xc7, 0xe2, 0xc6, 0x62, 0x91, 0x95, 0x46, 0x16, - 0xca, 0xd5, 0xa0, 0x76, 0xa8, 0x86, 0xb5, 0x43, 0x75, 0x23, 0xac, 0x1d, 0x6a, 0x45, 0x7e, 0x11, - 0x3f, 0xf9, 0x6b, 0x45, 0xd1, 0x4f, 0x70, 0x14, 0x3d, 0x04, 0x59, 0x0a, 0x31, 0xd4, 0x57, 0xc1, - 0xac, 0x30, 0x49, 0xc7, 0x75, 0x1e, 0xcf, 0x3e, 0xb6, 0xc2, 0x18, 0x89, 0x85, 0xbc, 0xf4, 0xc0, - 0x32, 0x38, 0x9f, 0x89, 0x5a, 0x7a, 0xe4, 0x04, 0x18, 0x92, 0xd7, 0x4e, 0x11, 0x09, 0x48, 0x7e, - 0xa9, 0xef, 0x82, 0x57, 0x04, 0xcc, 0xa2, 0x6d, 0xaf, 0x21, 0xe2, 0xd3, 0xbb, 0xc8, 0xe6, 0x38, - 0xfc, 0x10, 0x6a, 0xad, 0x36, 0x62, 0xc6, 0x27, 0xfc, 0xe7, 0x8a, 0xb4, 0xa1, 0x0b, 0x9c, 0x54, - 0xea, 0x01, 0x38, 0xea, 0x21, 0xe2, 0xf3, 0x2c, 0xc3, 0xcb, 0x1f, 0x11, 0x11, 0xf2, 0xb9, 0x5a, - 0xc9, 0x94, 0x16, 0xb8, 0x8c, 0x40, 0x04, 0x97, 0x10, 0x45, 0x9c, 0xd3, 0xf6, 0xc5, 0x98, 0x17, - 0x23, 0x51, 0xff, 0xaf, 0x80, 0x33, 0x5d, 0xb9, 0xe0, 0xca, 0xae, 0x79, 0x61, 0xf2, 0xeb, 0xa7, - 0x95, 0x89, 0xe0, 0xda, 0x24, 0x29, 0x52, 0x12, 0xc4, 0x4a, 0xca, 0xf5, 0x2b, 0x24, 0x71, 0x92, - 0x14, 0x29, 0xf7, 0xf0, 0x0a, 0x38, 0x1c, 0x51, 0x6d, 0xe3, 0x96, 0x0c, 0xb7, 0x53, 0xd5, 0x76, - 0xf1, 0x57, 0x0d, 0x8a, 0xbf, 0xea, 0xda, 0xce, 0xa6, 0x4d, 0xcc, 0x9b, 0xb8, 0xa5, 0x47, 0x47, - 0x75, 0x13, 0xb7, 0xd4, 0x71, 0x00, 0xc5, 0xb9, 0xac, 0x21, 0x1f, 0xb5, 0x63, 0xe8, 0xfb, 0xe0, - 0x58, 0x6c, 0x55, 0x1e, 0xcb, 0x2a, 0x18, 0xf2, 0xc4, 0x8a, 0xac, 0xb0, 0xce, 0x67, 0x3c, 0x0b, - 0xce, 0x22, 0x1f, 0x1c, 0x09, 0xa0, 0xde, 0x92, 0xf1, 0x10, 0x2b, 0x52, 0xee, 0x78, 0x0c, 0x5b, - 0xab, 0x4e, 0x94, 0x29, 0xb2, 0x97, 0x88, 0x0f, 0x64, 0xd0, 0x77, 0x83, 0x8b, 0x6a, 0xa0, 0xd3, - 0x9d, 0x6f, 0x7e, 0xe2, 0xbc, 0x70, 0x78, 0x17, 0x26, 0x3b, 0x1e, 0xff, 0xf8, 0x01, 0x62, 0xaa, - 0x2e, 0x82, 0xa9, 0x98, 0xc8, 0x7d, 0x68, 0xfd, 0xe9, 0x21, 0x30, 0xbd, 0x0b, 0x46, 0xf4, 0x57, - 0xaf, 0x4f, 0x51, 0x32, 0x42, 0x0a, 0x39, 0x23, 0x04, 0x96, 0xc0, 0xa0, 0x28, 0x8a, 0x44, 0x6c, - 0xf5, 0xd7, 0x0a, 0x25, 0x45, 0x0f, 0x16, 0xe0, 0x5b, 0x60, 0xc0, 0xe7, 0x39, 0x6e, 0x40, 0x68, - 0x73, 0x8e, 0x9f, 0xef, 0x9f, 0x9e, 0x56, 0x26, 0x83, 0x32, 0x90, 0x5a, 0xdb, 0x55, 0xe2, 0x6a, - 0x0d, 0xc4, 0xb6, 0xaa, 0xef, 0xe2, 0x3a, 0x32, 0x5b, 0xd7, 0xb0, 0x59, 0x52, 0x74, 0xc1, 0x02, - 0xcf, 0x81, 0xb1, 0x48, 0xab, 0x00, 0x7d, 0x50, 0xe4, 0xd7, 0xd1, 0x70, 0x55, 0x14, 0x5b, 0xf0, - 0x3e, 0x28, 0x45, 0x64, 0xa6, 0xdb, 0x68, 0x10, 0x4a, 0x89, 0xeb, 0x18, 0x42, 0xea, 0x90, 0x90, - 0x7a, 0x36, 0x83, 0x54, 0xfd, 0x44, 0x08, 0xb2, 0x14, 0x61, 0xe8, 0x5c, 0x8b, 0xfb, 0xa0, 0x14, - 0xb9, 0x36, 0x09, 0x7f, 0x28, 0x07, 0x7c, 0x08, 0x92, 0x80, 0xbf, 0x09, 0x46, 0x2c, 0x4c, 0x4d, - 0x9f, 0x78, 0xa2, 0x4c, 0x2e, 0x0a, 0xcf, 0x9f, 0x0d, 0xcb, 0xe4, 0xb0, 0x9f, 0x0a, 0x6b, 0xe4, - 0x6b, 0x6d, 0x52, 0x79, 0x57, 0x3a, 0xb9, 0xe1, 0x7d, 0x70, 0x32, 0xd2, 0xd5, 0xf5, 0xb0, 0x2f, - 0x8a, 0xcf, 0x30, 0x1e, 0x44, 0x89, 0x58, 0x3b, 0xf3, 0xd5, 0xe7, 0x17, 0x4e, 0x4b, 0xf4, 0x28, - 0x7e, 0x64, 0x1c, 0xac, 0x33, 0x9f, 0x38, 0x75, 0x7d, 0x22, 0xc4, 0xb8, 0x23, 0x21, 0xc2, 0x30, - 0x39, 0x01, 0x86, 0x7e, 0x80, 0x88, 0x8d, 0x2d, 0x51, 0x55, 0x16, 0x75, 0xf9, 0x05, 0x2f, 0x82, - 0x21, 0xde, 0x53, 0xed, 0x50, 0x51, 0x13, 0x8e, 0x2d, 0xa8, 0xbb, 0xa9, 0x5f, 0x73, 0x1d, 0x6b, - 0x5d, 0x50, 0xea, 0x92, 0x03, 0x6e, 0x80, 0x28, 0x1a, 0x0d, 0xe6, 0x6e, 0x63, 0x27, 0xa8, 0x18, - 0x87, 0x6b, 0xe7, 0xa5, 0x57, 0x8f, 0xbf, 0xe8, 0xd5, 0x55, 0x87, 0x7d, 0xf5, 0xf9, 0x05, 0x20, - 0x85, 0xac, 0x3a, 0x4c, 0x1f, 0x0b, 0x31, 0x36, 0x04, 0x04, 0x0f, 0x9d, 0x08, 0x35, 0x08, 0x9d, - 0xd1, 0x20, 0x74, 0xc2, 0xd5, 0x20, 0x74, 0x5e, 0x07, 0x13, 0xf2, 0xf6, 0x62, 0x6a, 0x98, 0x3b, - 0xbe, 0xcf, 0xfb, 0x07, 0xec, 0xb9, 0xe6, 0x96, 0xa8, 0x2f, 0x8b, 0xfa, 0xf1, 0x68, 0x7b, 0x29, - 0xd8, 0x5d, 0xe6, 0x9b, 0xea, 0xc7, 0x0a, 0xa8, 0xec, 0x7a, 0xaf, 0x65, 0xfa, 0xc0, 0x00, 0xb4, - 0x33, 0x83, 0x7c, 0x97, 0x96, 0x33, 0xe5, 0xc2, 0x6e, 0xb7, 0x5d, 0xef, 0x00, 0x56, 0x1f, 0x80, - 0xb9, 0x94, 0x46, 0x2e, 0xa2, 0xbd, 0x81, 0xe8, 0x86, 0x2b, 0xbf, 0xf0, 0xc1, 0x14, 0xae, 0xea, - 0x5d, 0x30, 0x9f, 0x43, 0xa4, 0x74, 0xc7, 0x99, 0x8e, 0x14, 0x43, 0xac, 0x30, 0x79, 0x8e, 0xb4, - 0x13, 0x9d, 0x28, 0x4a, 0xcf, 0xa7, 0x97, 0xb9, 0xf1, 0x3b, 0x93, 0x35, 0x75, 0xa6, 0xda, 0x59, - 0xc8, 0x6e, 0x67, 0x1d, 0xbc, 0x9a, 0x4d, 0x1d, 0x69, 0xe2, 0x1b, 0x32, 0xd5, 0x29, 0xd9, 0xb3, - 0x82, 0x60, 0x50, 0x55, 0x99, 0xe1, 0x6b, 0xb6, 0x6b, 0x6e, 0xd3, 0xf7, 0x1d, 0x46, 0xec, 0xdb, - 0xf8, 0x51, 0x10, 0x6b, 0xe1, 0x6b, 0x7b, 0x4f, 0x16, 0xec, 0xe9, 0x34, 0x52, 0x83, 0xd7, 0xc0, - 0xc4, 0xa6, 0xd8, 0x37, 0x76, 0x38, 0x81, 0x21, 0x2a, 0xce, 0x20, 0x9e, 0x15, 0xd1, 0xad, 0x8d, - 0x6f, 0xa6, 0xb0, 0xab, 0x8b, 0xb2, 0xfa, 0x5e, 0x8a, 0x5c, 0xb7, 0xe2, 0xbb, 0x8d, 0x25, 0xd9, - 0x3d, 0x87, 0xee, 0x8e, 0x75, 0xd8, 0x4a, 0xbc, 0xc3, 0x56, 0x57, 0xc0, 0xd9, 0x3d, 0x21, 0xda, - 0xa5, 0xf5, 0xde, 0xaf, 0xdd, 0x3b, 0xb2, 0x6e, 0x8f, 0xc5, 0x56, 0xe6, 0xb7, 0xf2, 0xb7, 0xfd, - 0x69, 0x73, 0x98, 0xcc, 0xd2, 0x63, 0xf3, 0x85, 0x42, 0x7c, 0xbe, 0x70, 0x16, 0x8c, 0xba, 0x0f, - 0x9d, 0x8e, 0x40, 0xea, 0x17, 0xfb, 0x87, 0xc5, 0x62, 0x98, 0x20, 0xa3, 0x76, 0x7c, 0x60, 0xb7, - 0x76, 0x7c, 0xf0, 0x20, 0xdb, 0xf1, 0x8f, 0xc0, 0x08, 0x71, 0x08, 0x33, 0x64, 0xbd, 0x35, 0x24, - 0xb0, 0x97, 0x73, 0x61, 0xaf, 0x3a, 0x84, 0x11, 0x64, 0x93, 0x1f, 0x8a, 0x51, 0x8b, 0xa8, 0xc2, - 0x78, 0xdf, 0x42, 0x75, 0xc0, 0x91, 0x83, 0xaa, 0x0c, 0x36, 0xc0, 0x78, 0x30, 0xf2, 0xa0, 0x5b, - 0xc8, 0x23, 0x4e, 0x3d, 0x14, 0x78, 0x48, 0x08, 0x7c, 0x3b, 0x5b, 0x81, 0xc7, 0x01, 0xd6, 0x03, - 0xfe, 0x0e, 0x31, 0xd0, 0x4b, 0xae, 0xd3, 0x85, 0xbf, 0x9f, 0x06, 0x83, 0xe2, 0x14, 0xe1, 0x3f, - 0x15, 0x30, 0x9e, 0x36, 0xd5, 0x83, 0x57, 0xf3, 0x27, 0xd2, 0xf8, 0x40, 0xb1, 0xbc, 0xd8, 0x03, - 0x42, 0x10, 0x4e, 0xea, 0x8d, 0x1f, 0xfd, 0xe1, 0x1f, 0x3f, 0x2d, 0xd4, 0xe0, 0xd5, 0xee, 0xe3, - 0xe7, 0x28, 0xec, 0xe4, 0xd8, 0x50, 0x7b, 0xdc, 0x11, 0x88, 0x4f, 0xe0, 0x9f, 0x15, 0x59, 0x4b, - 0xc7, 0x53, 0x2a, 0xbc, 0x92, 0x5f, 0xc9, 0xd8, 0xe4, 0xb1, 0x7c, 0x75, 0xff, 0x00, 0xd2, 0xc8, - 0x45, 0x61, 0xe4, 0xdb, 0xf0, 0xad, 0x1c, 0x46, 0x06, 0x03, 0x40, 0xed, 0xb1, 0x08, 0xff, 0x27, - 0xf0, 0xd3, 0x82, 0xbc, 0x95, 0xa9, 0xe3, 0x0b, 0xb8, 0x92, 0x5d, 0xc7, 0xbd, 0xc6, 0x31, 0xe5, - 0xeb, 0x3d, 0xe3, 0x48, 0x93, 0x37, 0x85, 0xc9, 0xdf, 0x85, 0xf7, 0x32, 0xfc, 0xac, 0x10, 0x8d, - 0xf8, 0x62, 0x7d, 0x58, 0xfc, 0x78, 0xb5, 0xc7, 0xc9, 0x57, 0x28, 0xcd, 0x27, 0x9d, 0xcd, 0xc3, - 0xbe, 0x7c, 0x92, 0x32, 0xc1, 0xd9, 0x97, 0x4f, 0xd2, 0x46, 0x2f, 0xfb, 0xf3, 0x49, 0xcc, 0xec, - 0xa4, 0x4f, 0x92, 0x8d, 0xeb, 0x13, 0xf8, 0x3b, 0x45, 0xf6, 0x99, 0xb1, 0xb1, 0x0c, 0xbc, 0x9c, - 0xdd, 0x86, 0xb4, 0x69, 0x4f, 0xf9, 0xca, 0xbe, 0xf9, 0xa5, 0xed, 0x6f, 0x0a, 0xdb, 0x17, 0xe0, - 0x5c, 0x77, 0xdb, 0x99, 0x04, 0x08, 0x7e, 0x63, 0x80, 0x3f, 0x2b, 0xc8, 0x67, 0x71, 0xef, 0x39, - 0x0b, 0xbc, 0x93, 0x5d, 0xc5, 0x4c, 0xf3, 0x9d, 0xf2, 0xda, 0xc1, 0x01, 0x4a, 0x27, 0xdc, 0x14, - 0x4e, 0x58, 0x86, 0x4b, 0xdd, 0x9d, 0xe0, 0x47, 0x88, 0xed, 0x5b, 0x11, 0x1b, 0xde, 0xc2, 0x1f, - 0x17, 0x64, 0xc5, 0xb1, 0xe7, 0xa4, 0x07, 0xde, 0xce, 0x6e, 0x45, 0x96, 0x09, 0x54, 0xf9, 0xce, - 0x81, 0xe1, 0x49, 0xa7, 0x2c, 0x0b, 0xa7, 0x5c, 0x81, 0x97, 0xba, 0x3b, 0x45, 0x46, 0xb9, 0xe1, - 0x71, 0xd4, 0x44, 0xfa, 0xff, 0xb5, 0x02, 0x46, 0x3a, 0x46, 0x29, 0xf0, 0x8d, 0xec, 0x7a, 0xc6, - 0x46, 0x32, 0xe5, 0x37, 0xf3, 0x33, 0x4a, 0x4b, 0xe6, 0x84, 0x25, 0xb3, 0x70, 0xa6, 0xbb, 0x25, - 0xc1, 0xe3, 0xdf, 0x8e, 0xed, 0xbd, 0xc7, 0x29, 0x79, 0x62, 0x3b, 0xd3, 0x9c, 0x27, 0x4f, 0x6c, - 0x67, 0x9b, 0xf4, 0xe4, 0x89, 0x6d, 0x97, 0x83, 0x18, 0xc4, 0x31, 0xda, 0x2d, 0x58, 0xe2, 0x30, - 0x7f, 0x53, 0x90, 0x43, 0xd1, 0x2c, 0xed, 0x11, 0x7c, 0x7f, 0xbf, 0x0f, 0xf4, 0x9e, 0x1d, 0x5e, - 0xf9, 0xee, 0x41, 0xc3, 0x4a, 0x4f, 0xdd, 0x13, 0x9e, 0xda, 0x80, 0x7a, 0xee, 0x6a, 0xc0, 0xf0, - 0xb0, 0xdf, 0x76, 0x5a, 0xda, 0x93, 0xf8, 0xab, 0x02, 0x78, 0x39, 0x4b, 0xbf, 0x05, 0xd7, 0x7a, - 0x78, 0xe8, 0x53, 0x3b, 0xc9, 0xf2, 0x7b, 0x07, 0x88, 0x28, 0x3d, 0x65, 0x0a, 0x4f, 0xdd, 0x87, - 0x1f, 0xe6, 0xf1, 0x54, 0x7c, 0xbc, 0xd4, 0xbd, 0x8a, 0xf8, 0xaf, 0x02, 0x26, 0x76, 0x99, 0x16, - 0xc0, 0xa5, 0x5e, 0x66, 0x0d, 0xa1, 0x63, 0xae, 0xf5, 0x06, 0x92, 0xff, 0x7e, 0x45, 0x16, 0xef, - 0x7a, 0xbf, 0xfe, 0xad, 0xc8, 0x16, 0x31, 0xad, 0x13, 0x86, 0x39, 0x26, 0x2c, 0x7b, 0x74, 0xdb, - 0xe5, 0x95, 0x5e, 0x61, 0xf2, 0x57, 0xcf, 0xbb, 0x34, 0xee, 0xf0, 0x7f, 0xc9, 0x9f, 0xea, 0xe3, - 0xad, 0x35, 0xbc, 0x9e, 0xff, 0x88, 0x52, 0xfb, 0xfb, 0xf2, 0x8d, 0xde, 0x81, 0x7a, 0xe8, 0x19, - 0x88, 0xa5, 0x3d, 0x8e, 0xc6, 0x0b, 0x4f, 0xe0, 0x5f, 0xc2, 0x5a, 0x30, 0x96, 0x9e, 0xf2, 0xd4, - 0x82, 0x69, 0x13, 0x84, 0xf2, 0x95, 0x7d, 0xf3, 0x4b, 0xd3, 0x56, 0x84, 0x69, 0x57, 0xe1, 0xe5, - 0xbc, 0x09, 0x30, 0x1e, 0xc5, 0xb5, 0x0f, 0xbe, 0x78, 0x36, 0xa5, 0x7c, 0xf9, 0x6c, 0x4a, 0xf9, - 0xdb, 0xb3, 0x29, 0xe5, 0x93, 0xe7, 0x53, 0x7d, 0x5f, 0x3e, 0x9f, 0xea, 0xfb, 0xe3, 0xf3, 0xa9, - 0xbe, 0x7b, 0x97, 0xea, 0x84, 0x6d, 0xed, 0x6c, 0x56, 0x4d, 0xb7, 0x21, 0xff, 0xe7, 0xa6, 0x43, - 0xd4, 0x85, 0x48, 0x54, 0xf3, 0x75, 0xed, 0x51, 0xa2, 0xf6, 0x6c, 0x79, 0x98, 0x6e, 0x0e, 0x89, - 0x5f, 0x0f, 0xbf, 0xf3, 0x4d, 0x00, 0x00, 0x00, 0xff, 0xff, 0x98, 0xe8, 0xa5, 0x6b, 0x13, 0x25, - 0x00, 0x00, + 0xb7, 0x86, 0x2f, 0x3d, 0xe5, 0xd0, 0x02, 0x09, 0x82, 0x9e, 0x9b, 0x73, 0x0f, 0x45, 0x51, 0x04, + 0xfd, 0x07, 0x7a, 0xc9, 0xad, 0x69, 0x7a, 0x29, 0x5a, 0xd4, 0x6d, 0xed, 0x16, 0xe8, 0xa5, 0x87, + 0xa6, 0xfd, 0x03, 0x82, 0x19, 0x0e, 0xb9, 0x4b, 0x9a, 0xd2, 0x92, 0x5a, 0xdd, 0xc4, 0x99, 0xf7, + 0x7e, 0xf3, 0xde, 0x9b, 0xf7, 0xde, 0xbc, 0xf7, 0x56, 0x40, 0x23, 0x0e, 0xc3, 0xbe, 0xb9, 0x85, + 0x88, 0x63, 0x50, 0x6c, 0xee, 0xf8, 0x84, 0xb5, 0x34, 0xd3, 0x6c, 0x6a, 0x9e, 0xef, 0x36, 0x89, + 0x85, 0x7d, 0xad, 0x39, 0xaf, 0x3d, 0xd8, 0xc1, 0x7e, 0xab, 0xea, 0xf9, 0x2e, 0x73, 0xe1, 0xd9, + 0x14, 0x86, 0xaa, 0x69, 0x36, 0xab, 0x21, 0x43, 0xb5, 0x39, 0x5f, 0x3e, 0x55, 0x77, 0xdd, 0xba, + 0x8d, 0x35, 0xe4, 0x11, 0x0d, 0x39, 0x8e, 0xcb, 0x10, 0x23, 0xae, 0x43, 0x03, 0x88, 0xf2, 0x78, + 0xdd, 0xad, 0xbb, 0xe2, 0x4f, 0x8d, 0xff, 0x25, 0x57, 0x2b, 0x92, 0x47, 0x7c, 0x6d, 0xee, 0x7c, + 0xa4, 0x31, 0xd2, 0xc0, 0x94, 0xa1, 0x86, 0x27, 0x09, 0x16, 0xb2, 0x88, 0x1a, 0x49, 0x11, 0xf0, + 0xcc, 0xed, 0xc6, 0xd3, 0x9c, 0xd7, 0xe8, 0x16, 0xf2, 0xb1, 0x65, 0x98, 0xae, 0x43, 0x77, 0x1a, + 0x11, 0xc7, 0xb9, 0x3d, 0x38, 0x1e, 0x12, 0x1f, 0x4b, 0xb2, 0x53, 0x0c, 0x3b, 0x16, 0xf6, 0x1b, + 0xc4, 0x61, 0x9a, 0xe9, 0xb7, 0x3c, 0xe6, 0x6a, 0xdb, 0xb8, 0x15, 0x6a, 0x78, 0xd2, 0x74, 0x69, + 0xc3, 0xa5, 0x46, 0xa0, 0x64, 0xf0, 0x21, 0xb7, 0x5e, 0x0e, 0xbe, 0x34, 0xca, 0xd0, 0x36, 0x71, + 0xea, 0x5a, 0x73, 0x7e, 0x13, 0x33, 0x34, 0x1f, 0x7e, 0x4b, 0xaa, 0x59, 0x49, 0xb5, 0x89, 0x28, + 0x0e, 0xcc, 0x1f, 0x11, 0x7a, 0xa8, 0x4e, 0x1c, 0x61, 0xcf, 0x80, 0x56, 0xbd, 0x0c, 0x26, 0xdf, + 0xe3, 0x14, 0x4b, 0x52, 0x91, 0xeb, 0xd8, 0xc1, 0x94, 0x50, 0x1d, 0x3f, 0xd8, 0xc1, 0x94, 0xc1, + 0x0a, 0x18, 0x09, 0x55, 0x34, 0x88, 0x55, 0x52, 0xa6, 0x95, 0x99, 0x61, 0x1d, 0x84, 0x4b, 0xab, + 0x96, 0xfa, 0x18, 0x9c, 0x4a, 0xe7, 0xa7, 0x9e, 0xeb, 0x50, 0x0c, 0x3f, 0x04, 0xa3, 0xf5, 0x60, + 0xc9, 0xa0, 0x0c, 0x31, 0x2c, 0x20, 0x46, 0x16, 0xe6, 0xaa, 0xbb, 0x79, 0x42, 0x73, 0xbe, 0x9a, + 0xc0, 0x5a, 0xe7, 0x7c, 0xb5, 0x81, 0x2f, 0x9f, 0x56, 0xfa, 0xf4, 0xc3, 0xf5, 0x8e, 0x35, 0xf5, + 0x57, 0x0a, 0x28, 0xc7, 0x4e, 0x5f, 0xe2, 0x78, 0x91, 0xf0, 0x37, 0xc0, 0xa0, 0xb7, 0x85, 0x68, + 0x70, 0xe6, 0xd8, 0xc2, 0x42, 0x35, 0x83, 0xf7, 0x45, 0x87, 0xaf, 0x71, 0x4e, 0x3d, 0x00, 0x80, + 0x2b, 0x00, 0xb4, 0x2d, 0x57, 0x2a, 0x08, 0x15, 0xbe, 0x53, 0x95, 0x57, 0xc3, 0xcd, 0x5c, 0x0d, + 0xbc, 0x5c, 0x9a, 0xb9, 0xba, 0x86, 0xea, 0x58, 0x4a, 0xa1, 0x77, 0x70, 0xaa, 0xbf, 0x54, 0x12, + 0xe6, 0x0e, 0x05, 0x96, 0xd6, 0xaa, 0x81, 0x21, 0x21, 0x1e, 0x2d, 0x29, 0xd3, 0xfd, 0x33, 0x23, + 0x0b, 0xb3, 0xd9, 0x44, 0xe6, 0xdb, 0xba, 0xe4, 0x84, 0xd7, 0x53, 0x64, 0xfd, 0x6e, 0x57, 0x59, + 0x03, 0x01, 0x62, 0xc2, 0x7e, 0x36, 0x08, 0x06, 0x05, 0x34, 0x3c, 0x09, 0x8a, 0x81, 0x08, 0x91, + 0x0b, 0x1c, 0x12, 0xdf, 0xab, 0x16, 0x9c, 0x04, 0xc3, 0xa6, 0x4d, 0xb0, 0xc3, 0xf8, 0x5e, 0x41, + 0xec, 0x15, 0x83, 0x85, 0x55, 0x0b, 0x1e, 0x03, 0x83, 0xcc, 0xf5, 0x8c, 0xdb, 0xa5, 0xfe, 0x69, + 0x65, 0x66, 0x54, 0x1f, 0x60, 0xae, 0x77, 0x1b, 0xce, 0x02, 0xd8, 0x20, 0x8e, 0xe1, 0xb9, 0x0f, + 0xb9, 0x4f, 0x39, 0x46, 0x40, 0x31, 0x30, 0xad, 0xcc, 0xf4, 0xeb, 0x63, 0x0d, 0xe2, 0xac, 0xf1, + 0x8d, 0x55, 0x67, 0x83, 0xd3, 0xce, 0x81, 0xf1, 0x26, 0xb2, 0x89, 0x85, 0x98, 0xeb, 0x53, 0xc9, + 0x62, 0x22, 0xaf, 0x34, 0x28, 0xf0, 0x60, 0x7b, 0x4f, 0x30, 0x2d, 0x21, 0x0f, 0xce, 0x82, 0xa3, + 0xd1, 0xaa, 0x41, 0x31, 0x13, 0xe4, 0x43, 0x82, 0xfc, 0x48, 0xb4, 0xb1, 0x8e, 0x19, 0xa7, 0x3d, + 0x05, 0x86, 0x91, 0x6d, 0xbb, 0x0f, 0x6d, 0x42, 0x59, 0xe9, 0xd0, 0x74, 0xff, 0xcc, 0xb0, 0xde, + 0x5e, 0x80, 0x65, 0x50, 0xb4, 0xb0, 0xd3, 0x12, 0x9b, 0x45, 0xb1, 0x19, 0x7d, 0xc3, 0xf1, 0xd0, + 0xb3, 0x86, 0x85, 0xc6, 0xd2, 0x4b, 0x3e, 0x00, 0xc5, 0x06, 0x66, 0xc8, 0x42, 0x0c, 0x95, 0x80, + 0xb0, 0xfb, 0x6b, 0xb9, 0x5c, 0xee, 0x96, 0x64, 0x96, 0xbe, 0x1e, 0x81, 0x71, 0x23, 0x73, 0x93, + 0xf1, 0x28, 0xc7, 0xa5, 0x91, 0x69, 0x65, 0x66, 0x40, 0x2f, 0x36, 0x88, 0xb3, 0xce, 0xbf, 0x61, + 0x15, 0x1c, 0x13, 0x42, 0x1b, 0xc4, 0x41, 0x26, 0x23, 0x4d, 0x6c, 0x34, 0x91, 0x4d, 0x4b, 0x87, + 0xa7, 0x95, 0x99, 0xa2, 0x7e, 0x54, 0x6c, 0xad, 0xca, 0x9d, 0xbb, 0xc8, 0xa6, 0xc9, 0x90, 0x1e, + 0x4d, 0x86, 0x34, 0x7c, 0x04, 0x4e, 0x46, 0x56, 0xc0, 0x96, 0xe1, 0xe3, 0x87, 0xc8, 0xb7, 0x0c, + 0x0b, 0x3b, 0x6e, 0x83, 0x96, 0xc6, 0x84, 0x5e, 0xef, 0x64, 0xd2, 0x6b, 0xb1, 0x8d, 0xa2, 0x0b, + 0x90, 0x6b, 0x02, 0x43, 0x9f, 0x40, 0xe9, 0x1b, 0x50, 0x05, 0x87, 0x3d, 0x9f, 0xb8, 0x1c, 0x4c, + 0x98, 0xfd, 0x88, 0x30, 0x7b, 0x6c, 0x4d, 0xfd, 0x99, 0x02, 0xce, 0x88, 0x10, 0xba, 0x1b, 0xde, + 0x66, 0x68, 0xbe, 0x45, 0xcb, 0xf2, 0xc3, 0xd0, 0xbf, 0x04, 0x5e, 0x0a, 0x25, 0x31, 0x90, 0x65, + 0xf9, 0x98, 0xd2, 0xc0, 0x73, 0x6b, 0xf0, 0x9b, 0xa7, 0x95, 0xb1, 0x16, 0x6a, 0xd8, 0x17, 0x55, + 0xb9, 0xa1, 0xea, 0x47, 0x42, 0xda, 0xc5, 0x60, 0x25, 0x69, 0xa3, 0x42, 0xd2, 0x46, 0x17, 0x8b, + 0x1f, 0x7f, 0x5e, 0xe9, 0xfb, 0xf7, 0xe7, 0x95, 0x3e, 0xf5, 0x0e, 0x50, 0xf7, 0x12, 0x47, 0x06, + 0xf6, 0x2b, 0xe0, 0xa5, 0x08, 0x30, 0x26, 0x8f, 0x7e, 0xc4, 0xec, 0xa0, 0xe7, 0xd2, 0xbc, 0xa8, + 0xe0, 0x5a, 0x87, 0x74, 0x1d, 0x0a, 0xa6, 0x03, 0xa6, 0x2b, 0x98, 0x38, 0xa4, 0x27, 0x05, 0xe3, + 0xe2, 0xb4, 0x15, 0x4c, 0x37, 0xf8, 0x0b, 0xc6, 0x55, 0x27, 0xc1, 0x49, 0x01, 0xb8, 0xb1, 0xe5, + 0xbb, 0x8c, 0xd9, 0x58, 0xe4, 0x72, 0xa9, 0x97, 0xfa, 0x87, 0x30, 0xa5, 0x27, 0x76, 0xe5, 0x31, + 0x15, 0x30, 0x42, 0x6d, 0x44, 0xb7, 0x8c, 0x06, 0x66, 0xd8, 0x17, 0x27, 0xf4, 0xeb, 0x40, 0x2c, + 0xdd, 0xe2, 0x2b, 0x70, 0x01, 0x1c, 0xef, 0x20, 0x30, 0x84, 0xa7, 0x21, 0xc7, 0xc4, 0x42, 0xc5, + 0x7e, 0xfd, 0x58, 0x9b, 0x74, 0x31, 0xdc, 0x82, 0x3f, 0x00, 0x25, 0x07, 0x3f, 0x62, 0x86, 0x8f, + 0x3d, 0x1b, 0x3b, 0x84, 0x6e, 0x19, 0x26, 0x72, 0x2c, 0xae, 0x2c, 0x16, 0x99, 0x6b, 0x64, 0xa1, + 0x5c, 0x0d, 0xea, 0x8b, 0x6a, 0x58, 0x5f, 0x54, 0x37, 0xc2, 0xfa, 0xa2, 0x56, 0xe4, 0xc1, 0xfa, + 0xc9, 0xdf, 0x2a, 0x8a, 0x7e, 0x82, 0xa3, 0xe8, 0x21, 0xc8, 0x52, 0x88, 0xa1, 0xbe, 0x0a, 0x66, + 0x85, 0x4a, 0x3a, 0xae, 0x73, 0x9f, 0xf7, 0xb1, 0x15, 0xfa, 0x48, 0x2c, 0x2c, 0xa4, 0x05, 0x96, + 0xc1, 0xf9, 0x4c, 0xd4, 0xd2, 0x22, 0x27, 0xc0, 0x90, 0x0c, 0x4d, 0x45, 0x44, 0x8b, 0xfc, 0x52, + 0xdf, 0x05, 0xaf, 0x08, 0x98, 0x45, 0xdb, 0x5e, 0x43, 0xc4, 0xa7, 0x77, 0x91, 0xcd, 0x71, 0xf8, + 0x25, 0xd4, 0x5a, 0x6d, 0xc4, 0x8c, 0xcf, 0xfc, 0x2f, 0x14, 0xa9, 0x43, 0x17, 0x38, 0x29, 0xd4, + 0x03, 0x70, 0xd4, 0x43, 0xc4, 0xe7, 0x99, 0x88, 0x97, 0x48, 0xc2, 0x23, 0xe4, 0x93, 0xb6, 0x92, + 0x29, 0x75, 0xf0, 0x33, 0x82, 0x23, 0xf8, 0x09, 0x91, 0xc7, 0x39, 0x6d, 0x5b, 0x8c, 0x79, 0x31, + 0x12, 0xf5, 0xff, 0x0a, 0x38, 0xd3, 0x95, 0x0b, 0xae, 0xec, 0x9a, 0x17, 0x26, 0xbf, 0x79, 0x5a, + 0x99, 0x08, 0xc2, 0x26, 0x49, 0x91, 0x92, 0x20, 0x56, 0x52, 0xc2, 0xaf, 0x90, 0xc4, 0x49, 0x52, + 0xa4, 0xc4, 0xe1, 0x15, 0x70, 0x38, 0xa2, 0xda, 0xc6, 0x2d, 0xe9, 0x6e, 0xa7, 0xaa, 0xed, 0x02, + 0xb1, 0x1a, 0x14, 0x88, 0xd5, 0xb5, 0x9d, 0x4d, 0x9b, 0x98, 0x37, 0x71, 0x4b, 0x8f, 0xae, 0xea, + 0x26, 0x6e, 0xa9, 0xe3, 0x00, 0x8a, 0x7b, 0x59, 0x43, 0x3e, 0x6a, 0xfb, 0xd0, 0x0f, 0xc1, 0xb1, + 0xd8, 0xaa, 0xbc, 0x96, 0x55, 0x30, 0xe4, 0x89, 0x15, 0x59, 0x85, 0x9d, 0xcf, 0x78, 0x17, 0x9c, + 0x45, 0x3e, 0x4a, 0x12, 0x40, 0xbd, 0x25, 0xfd, 0x21, 0x56, 0xc8, 0xdc, 0xf1, 0x18, 0xb6, 0x56, + 0x9d, 0x28, 0x53, 0x64, 0x2f, 0x23, 0x1f, 0x48, 0xa7, 0xef, 0x06, 0x17, 0xd5, 0x49, 0xa7, 0x3b, + 0xeb, 0x82, 0xc4, 0x7d, 0xe1, 0x30, 0x16, 0x26, 0x3b, 0x0a, 0x84, 0xf8, 0x05, 0x62, 0xaa, 0x2e, + 0x82, 0xa9, 0xd8, 0x91, 0xfb, 0x90, 0xfa, 0xd3, 0x43, 0x60, 0x7a, 0x17, 0x8c, 0xe8, 0xaf, 0x5e, + 0x9f, 0xa2, 0xa4, 0x87, 0x14, 0x72, 0x7a, 0x08, 0x2c, 0x81, 0x41, 0x51, 0x38, 0x09, 0xdf, 0xea, + 0xaf, 0x15, 0x4a, 0x8a, 0x1e, 0x2c, 0xc0, 0xb7, 0xc0, 0x80, 0xcf, 0x73, 0xdc, 0x80, 0x90, 0xe6, + 0x1c, 0xbf, 0xdf, 0x3f, 0x3f, 0xad, 0x4c, 0x06, 0xa5, 0x22, 0xb5, 0xb6, 0xab, 0xc4, 0xd5, 0x1a, + 0x88, 0x6d, 0x55, 0xdf, 0xc5, 0x75, 0x64, 0xb6, 0xae, 0x61, 0xb3, 0xa4, 0xe8, 0x82, 0x05, 0x9e, + 0x03, 0x63, 0x91, 0x54, 0x01, 0xfa, 0xa0, 0xc8, 0xaf, 0xa3, 0xe1, 0xaa, 0x28, 0xc8, 0xe0, 0x7d, + 0x50, 0x8a, 0xc8, 0x4c, 0xb7, 0xd1, 0x20, 0x94, 0x12, 0xd7, 0x31, 0xc4, 0xa9, 0x43, 0xe2, 0xd4, + 0xb3, 0x19, 0x4e, 0xd5, 0x4f, 0x84, 0x20, 0x4b, 0x11, 0x86, 0xce, 0xa5, 0xb8, 0x0f, 0x4a, 0x91, + 0x69, 0x93, 0xf0, 0x87, 0x72, 0xc0, 0x87, 0x20, 0x09, 0xf8, 0x9b, 0x60, 0xc4, 0xc2, 0xd4, 0xf4, + 0x89, 0x27, 0x4a, 0xe9, 0xa2, 0xb0, 0xfc, 0xd9, 0xb0, 0x94, 0x0e, 0x7b, 0xae, 0xb0, 0x8e, 0xbe, + 0xd6, 0x26, 0x95, 0xb1, 0xd2, 0xc9, 0x0d, 0xef, 0x83, 0x93, 0x91, 0xac, 0xae, 0x87, 0x7d, 0x51, + 0xa0, 0x86, 0xfe, 0x20, 0xca, 0xc8, 0xda, 0x99, 0xaf, 0xbf, 0xb8, 0x70, 0x5a, 0xa2, 0x47, 0xfe, + 0x23, 0xfd, 0x60, 0x9d, 0xf9, 0xc4, 0xa9, 0xeb, 0x13, 0x21, 0xc6, 0x1d, 0x09, 0x11, 0xba, 0xc9, + 0x09, 0x30, 0xf4, 0x23, 0x44, 0x6c, 0x6c, 0x89, 0xca, 0xb3, 0xa8, 0xcb, 0x2f, 0x78, 0x11, 0x0c, + 0xf1, 0xbe, 0x6b, 0x87, 0x8a, 0xba, 0x71, 0x6c, 0x41, 0xdd, 0x4d, 0xfc, 0x9a, 0xeb, 0x58, 0xeb, + 0x82, 0x52, 0x97, 0x1c, 0x70, 0x03, 0x44, 0xde, 0x68, 0x30, 0x77, 0x1b, 0x3b, 0x41, 0x55, 0x39, + 0x5c, 0x3b, 0x2f, 0xad, 0x7a, 0xfc, 0x45, 0xab, 0xae, 0x3a, 0xec, 0xeb, 0x2f, 0x2e, 0x00, 0x79, + 0xc8, 0xaa, 0xc3, 0xf4, 0xb1, 0x10, 0x63, 0x43, 0x40, 0x70, 0xd7, 0x89, 0x50, 0x03, 0xd7, 0x19, + 0x0d, 0x5c, 0x27, 0x5c, 0x0d, 0x5c, 0xe7, 0x75, 0x30, 0x21, 0xa3, 0x17, 0x53, 0xc3, 0xdc, 0xf1, + 0x7d, 0xde, 0x63, 0x60, 0xcf, 0x35, 0xb7, 0x44, 0x0d, 0x5a, 0xd4, 0x8f, 0x47, 0xdb, 0x4b, 0xc1, + 0xee, 0x32, 0xdf, 0x54, 0x3f, 0x56, 0x40, 0x65, 0xd7, 0xb8, 0x96, 0xe9, 0x03, 0x03, 0xd0, 0xce, + 0x0c, 0xf2, 0x5d, 0x5a, 0xce, 0x94, 0x0b, 0xbb, 0x45, 0xbb, 0xde, 0x01, 0xac, 0x3e, 0x00, 0x73, + 0x29, 0xcd, 0x5e, 0x44, 0x7b, 0x03, 0xd1, 0x0d, 0x57, 0x7e, 0xe1, 0x83, 0x29, 0x5c, 0xd5, 0xbb, + 0x60, 0x3e, 0xc7, 0x91, 0xd2, 0x1c, 0x67, 0x3a, 0x52, 0x0c, 0xb1, 0xc2, 0xe4, 0x39, 0xd2, 0x4e, + 0x74, 0xa2, 0x28, 0x3d, 0x9f, 0x5e, 0xe6, 0xc6, 0x63, 0x26, 0x6b, 0xea, 0x4c, 0xd5, 0xb3, 0x90, + 0x5d, 0xcf, 0x3a, 0x78, 0x35, 0x9b, 0x38, 0x52, 0xc5, 0x37, 0x64, 0xaa, 0x53, 0xb2, 0x67, 0x05, + 0xc1, 0xa0, 0xaa, 0x32, 0xc3, 0xd7, 0x6c, 0xd7, 0xdc, 0xa6, 0xef, 0x3b, 0x8c, 0xd8, 0xb7, 0xf1, + 0xa3, 0xc0, 0xd7, 0xc2, 0xd7, 0xf6, 0x9e, 0x2c, 0xd8, 0xd3, 0x69, 0xa4, 0x04, 0xaf, 0x81, 0x89, + 0x4d, 0xb1, 0x6f, 0xec, 0x70, 0x02, 0x43, 0x54, 0x9c, 0x81, 0x3f, 0x2b, 0xa2, 0xa3, 0x1b, 0xdf, + 0x4c, 0x61, 0x57, 0x17, 0x65, 0xf5, 0xbd, 0x14, 0x99, 0x6e, 0xc5, 0x77, 0x1b, 0x4b, 0xb2, 0xc3, + 0x0e, 0xcd, 0x1d, 0xeb, 0xc2, 0x95, 0x78, 0x17, 0xae, 0xae, 0x80, 0xb3, 0x7b, 0x42, 0xb4, 0x4b, + 0xeb, 0xbd, 0x5f, 0xbb, 0x77, 0x64, 0xdd, 0x1e, 0xf3, 0xad, 0xcc, 0x6f, 0xe5, 0xef, 0xfa, 0xd3, + 0x66, 0x35, 0x99, 0x4f, 0x8f, 0xcd, 0x20, 0x0a, 0xf1, 0x19, 0xc4, 0x59, 0x30, 0xea, 0x3e, 0x74, + 0x3a, 0x1c, 0xa9, 0x5f, 0xec, 0x1f, 0x16, 0x8b, 0x61, 0x82, 0x8c, 0x5a, 0xf6, 0x81, 0xdd, 0x5a, + 0xf6, 0xc1, 0x83, 0x6c, 0xd9, 0x3f, 0x02, 0x23, 0xc4, 0x21, 0xcc, 0x90, 0xf5, 0xd6, 0x90, 0xc0, + 0x5e, 0xce, 0x85, 0xbd, 0xea, 0x10, 0x46, 0x90, 0x4d, 0x7e, 0x2c, 0xc6, 0x31, 0xa2, 0x0a, 0xe3, + 0x7d, 0x0b, 0xd5, 0x01, 0x47, 0x0e, 0xaa, 0x32, 0xd8, 0x00, 0xe3, 0xc1, 0x58, 0x84, 0x6e, 0x21, + 0x8f, 0x38, 0xf5, 0xf0, 0xc0, 0x43, 0xe2, 0xc0, 0xb7, 0xb3, 0x15, 0x78, 0x1c, 0x60, 0x3d, 0xe0, + 0xef, 0x38, 0x06, 0x7a, 0xc9, 0x75, 0xba, 0xf0, 0x8f, 0xd3, 0x60, 0x50, 0xdc, 0x22, 0xfc, 0x97, + 0x02, 0xc6, 0xd3, 0x26, 0x7f, 0xf0, 0x6a, 0xfe, 0x44, 0x1a, 0x1f, 0x3a, 0x96, 0x17, 0x7b, 0x40, + 0x08, 0xdc, 0x49, 0xbd, 0xf1, 0x93, 0x3f, 0xfe, 0xf3, 0xb3, 0x42, 0x0d, 0x5e, 0xed, 0x3e, 0xa2, + 0x8e, 0xdc, 0x4e, 0x8e, 0x16, 0xb5, 0xc7, 0x1d, 0x8e, 0xf8, 0x04, 0xfe, 0x45, 0x91, 0xb5, 0x74, + 0x3c, 0xa5, 0xc2, 0x2b, 0xf9, 0x85, 0x8c, 0x4d, 0x27, 0xcb, 0x57, 0xf7, 0x0f, 0x20, 0x95, 0x5c, + 0x14, 0x4a, 0xbe, 0x0d, 0xdf, 0xca, 0xa1, 0x64, 0x30, 0x24, 0xd4, 0x1e, 0x0b, 0xf7, 0x7f, 0x02, + 0x3f, 0x2d, 0xc8, 0xa8, 0x4c, 0x1d, 0x5f, 0xc0, 0x95, 0xec, 0x32, 0xee, 0x35, 0x8e, 0x29, 0x5f, + 0xef, 0x19, 0x47, 0xaa, 0xbc, 0x29, 0x54, 0xfe, 0x3e, 0xbc, 0x97, 0xe1, 0xa7, 0x87, 0x68, 0x0c, + 0x18, 0xeb, 0xc3, 0xe2, 0xd7, 0xab, 0x3d, 0x4e, 0xbe, 0x42, 0x69, 0x36, 0xe9, 0x6c, 0x1e, 0xf6, + 0x65, 0x93, 0x94, 0x09, 0xce, 0xbe, 0x6c, 0x92, 0x36, 0x7a, 0xd9, 0x9f, 0x4d, 0x62, 0x6a, 0x27, + 0x6d, 0x92, 0x6c, 0x5c, 0x9f, 0xc0, 0xdf, 0x2b, 0xb2, 0xcf, 0x8c, 0x8d, 0x65, 0xe0, 0xe5, 0xec, + 0x3a, 0xa4, 0x4d, 0x7b, 0xca, 0x57, 0xf6, 0xcd, 0x2f, 0x75, 0x7f, 0x53, 0xe8, 0xbe, 0x00, 0xe7, + 0xba, 0xeb, 0xce, 0x24, 0x40, 0xf0, 0x3b, 0x04, 0xfc, 0x79, 0x41, 0x3e, 0x8b, 0x7b, 0xcf, 0x59, + 0xe0, 0x9d, 0xec, 0x22, 0x66, 0x9a, 0xef, 0x94, 0xd7, 0x0e, 0x0e, 0x50, 0x1a, 0xe1, 0xa6, 0x30, + 0xc2, 0x32, 0x5c, 0xea, 0x6e, 0x04, 0x3f, 0x42, 0x6c, 0x47, 0x45, 0x6c, 0xc0, 0x0b, 0x7f, 0x5a, + 0x90, 0x15, 0xc7, 0x9e, 0x93, 0x1e, 0x78, 0x3b, 0xbb, 0x16, 0x59, 0x26, 0x50, 0xe5, 0x3b, 0x07, + 0x86, 0x27, 0x8d, 0xb2, 0x2c, 0x8c, 0x72, 0x05, 0x5e, 0xea, 0x6e, 0x14, 0xe9, 0xe5, 0x86, 0xc7, + 0x51, 0x13, 0xe9, 0xff, 0x37, 0x0a, 0x18, 0xe9, 0x18, 0xa5, 0xc0, 0x37, 0xb2, 0xcb, 0x19, 0x1b, + 0xc9, 0x94, 0xdf, 0xcc, 0xcf, 0x28, 0x35, 0x99, 0x13, 0x9a, 0xcc, 0xc2, 0x99, 0xee, 0x9a, 0x04, + 0x8f, 0x7f, 0xdb, 0xb7, 0xf7, 0x1e, 0xa7, 0xe4, 0xf1, 0xed, 0x4c, 0x73, 0x9e, 0x3c, 0xbe, 0x9d, + 0x6d, 0xd2, 0x93, 0xc7, 0xb7, 0x5d, 0x0e, 0x62, 0x10, 0xc7, 0x68, 0xb7, 0x60, 0x89, 0xcb, 0xfc, + 0x6d, 0x41, 0x0e, 0x45, 0xb3, 0xb4, 0x47, 0xf0, 0xfd, 0xfd, 0x3e, 0xd0, 0x7b, 0x76, 0x78, 0xe5, + 0xbb, 0x07, 0x0d, 0x2b, 0x2d, 0x75, 0x4f, 0x58, 0x6a, 0x03, 0xea, 0xb9, 0xab, 0x01, 0xc3, 0xc3, + 0x7e, 0xdb, 0x68, 0x69, 0x4f, 0xe2, 0xaf, 0x0b, 0xe0, 0xe5, 0x2c, 0xfd, 0x16, 0x5c, 0xeb, 0xe1, + 0xa1, 0x4f, 0xed, 0x24, 0xcb, 0xef, 0x1d, 0x20, 0xa2, 0xb4, 0x94, 0x29, 0x2c, 0x75, 0x1f, 0x7e, + 0x98, 0xc7, 0x52, 0xf1, 0xf1, 0x52, 0xf7, 0x2a, 0xe2, 0xbf, 0x0a, 0x98, 0xd8, 0x65, 0x5a, 0x00, + 0x97, 0x7a, 0x99, 0x35, 0x84, 0x86, 0xb9, 0xd6, 0x1b, 0x48, 0xfe, 0xf8, 0x8a, 0x34, 0xde, 0x35, + 0xbe, 0xfe, 0xa3, 0xc8, 0x16, 0x31, 0xad, 0x13, 0x86, 0x39, 0x26, 0x2c, 0x7b, 0x74, 0xdb, 0xe5, + 0x95, 0x5e, 0x61, 0xf2, 0x57, 0xcf, 0xbb, 0x34, 0xee, 0xf0, 0x7f, 0xc9, 0x9f, 0xf3, 0xe3, 0xad, + 0x35, 0xbc, 0x9e, 0xff, 0x8a, 0x52, 0xfb, 0xfb, 0xf2, 0x8d, 0xde, 0x81, 0x7a, 0xe8, 0x19, 0x88, + 0xa5, 0x3d, 0x8e, 0xc6, 0x0b, 0x4f, 0xe0, 0x5f, 0xc3, 0x5a, 0x30, 0x96, 0x9e, 0xf2, 0xd4, 0x82, + 0x69, 0x13, 0x84, 0xf2, 0x95, 0x7d, 0xf3, 0x4b, 0xd5, 0x56, 0x84, 0x6a, 0x57, 0xe1, 0xe5, 0xbc, + 0x09, 0x30, 0xee, 0xc5, 0xb5, 0x0f, 0xbe, 0x7c, 0x36, 0xa5, 0x7c, 0xf5, 0x6c, 0x4a, 0xf9, 0xfb, + 0xb3, 0x29, 0xe5, 0x93, 0xe7, 0x53, 0x7d, 0x5f, 0x3d, 0x9f, 0xea, 0xfb, 0xd3, 0xf3, 0xa9, 0xbe, + 0x7b, 0x97, 0xea, 0x84, 0x6d, 0xed, 0x6c, 0x56, 0x4d, 0xb7, 0x21, 0xff, 0x2f, 0xa7, 0xe3, 0xa8, + 0x0b, 0xd1, 0x51, 0xcd, 0xd7, 0xb5, 0x47, 0x89, 0xda, 0xb3, 0xe5, 0x61, 0xba, 0x39, 0x24, 0x7e, + 0x3d, 0xfc, 0xde, 0xb7, 0x01, 0x00, 0x00, 0xff, 0xff, 0x19, 0x33, 0x42, 0x36, 0x37, 0x25, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2830,6 +2843,15 @@ func (m *Chain) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Prioritylist) > 0 { + for iNdEx := len(m.Prioritylist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Prioritylist[iNdEx]) + copy(dAtA[i:], m.Prioritylist[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Prioritylist[iNdEx]))) + i-- + dAtA[i] = 0x7a + } + } if m.AllowlistedRewardDenoms != nil { { size, err := m.AllowlistedRewardDenoms.MarshalToSizedBuffer(dAtA[:i]) @@ -4115,6 +4137,12 @@ func (m *Chain) Size() (n int) { l = m.AllowlistedRewardDenoms.Size() n += 1 + l + sovQuery(uint64(l)) } + if len(m.Prioritylist) > 0 { + for _, s := range m.Prioritylist { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } return n } @@ -5354,6 +5382,38 @@ func (m *Chain) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Prioritylist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Prioritylist = append(m.Prioritylist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) From 3c3db71c5cab78a18ad82746b94fcb78afc5a19c Mon Sep 17 00:00:00 2001 From: Jehan Date: Mon, 28 Oct 2024 07:44:48 -0700 Subject: [PATCH 03/11] docs: add spawn page to docs (#2338) * add spawn page * add .md extension to doc; change links * update dead link to new one --------- Co-authored-by: MSalopek --- .../consumer-development/app-integration.md | 3 +- .../changeover-procedure.md | 2 +- .../consumer-chain-governance.md | 2 +- .../consumer-genesis-transformation.md | 2 +- .../consumer-development/create-with-spawn.md | 107 ++++++++++++++++++ docs/docs/consumer-development/offboarding.md | 2 +- docs/docs/consumer-development/onboarding.md | 5 +- 7 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 docs/docs/consumer-development/create-with-spawn.md diff --git a/docs/docs/consumer-development/app-integration.md b/docs/docs/consumer-development/app-integration.md index 0671db05a7..57ec14fc48 100644 --- a/docs/docs/consumer-development/app-integration.md +++ b/docs/docs/consumer-development/app-integration.md @@ -1,5 +1,5 @@ --- -sidebar_position: 1 +sidebar_position: 2 --- # Developing an ICS consumer chain @@ -31,4 +31,3 @@ With these modules enabled, the consumer chain can mint its own governance token ## Standalone chain to consumer chain changeover See the [standalone chain to consumer chain changeover guide](./changeover-procedure.md) for more information on how to transition your standalone chain to a consumer chain. - diff --git a/docs/docs/consumer-development/changeover-procedure.md b/docs/docs/consumer-development/changeover-procedure.md index 240925fc0d..17b8faa713 100644 --- a/docs/docs/consumer-development/changeover-procedure.md +++ b/docs/docs/consumer-development/changeover-procedure.md @@ -1,5 +1,5 @@ --- -sidebar_position: 5 +sidebar_position: 6 --- # Changeover Procedure diff --git a/docs/docs/consumer-development/consumer-chain-governance.md b/docs/docs/consumer-development/consumer-chain-governance.md index c8586efe3a..86b56f8418 100644 --- a/docs/docs/consumer-development/consumer-chain-governance.md +++ b/docs/docs/consumer-development/consumer-chain-governance.md @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 3 --- # Consumer Chain Governance diff --git a/docs/docs/consumer-development/consumer-genesis-transformation.md b/docs/docs/consumer-development/consumer-genesis-transformation.md index d77e8ec559..360332aa25 100644 --- a/docs/docs/consumer-development/consumer-genesis-transformation.md +++ b/docs/docs/consumer-development/consumer-genesis-transformation.md @@ -1,5 +1,5 @@ --- -sidebar_position: 6 +sidebar_position: 7 --- # Consumer Genesis Transformation diff --git a/docs/docs/consumer-development/create-with-spawn.md b/docs/docs/consumer-development/create-with-spawn.md new file mode 100644 index 0000000000..07e202b041 --- /dev/null +++ b/docs/docs/consumer-development/create-with-spawn.md @@ -0,0 +1,107 @@ +--- +sidebar_position: 1 +--- + +# Create an ICS chain with Spawn + +## Requirements + +- [`go 1.22+`](https://go.dev/doc/install) +- [`Docker`](https://docs.docker.com/get-docker/) + +[MacOS + Ubuntu Setup](https://github.com/rollchains/spawn/blob/release/v0.50/docs/versioned_docs/version-v0.50.x/01-setup/01-system-setup.md) + +## Getting Started + +**Note:** This tutorial focuses on using the Spawn CLI to create an ICS consumer chain. For more complete documentation on Spawn, see the [Spawn documentation](https://rollchains.github.io/spawn/v0.50/). + +In this tutorial, we'll create and interact with a new Interchain security enabled blockchain called "consumer", with the token denomination "uconsu". + +1. Clone this repo and install + +```shell +git clone https://github.com/rollchains/spawn.git +cd spawn +git checkout v0.50.4 +make install +``` + +2. Create your chain using the `spawn` command and customize it to your needs! + +```shell +GITHUB_USERNAME= + +spawn new consumer \ +--consensus=interchain-security \ +--bech32=consu `# the prefix for addresses` \ +--denom=uconsu `# the coin denomination to create` \ +--bin=consumerd `# the name of the binary` \ +--disabled=tokenfactory,globalfee,ibc-packetforward,ibc-ratelimit,cosmwasm,wasm-light-client,optimistic-execution,ignite-cli `# disable features. [tokenfactory,globalfee,ibc-packetforward,ibc-ratelimit,cosmwasm,wasm-light-client,ignite-cli]` \ +--org=${GITHUB_USERNAME} `# the github username or organization to use for the module imports, optional` +``` + +> _NOTE:_ `spawn` creates a ready to use repository complete with `git` and GitHub CI. It can be quickly pushed to a new repository getting you and your team up and running quickly. + +3. Spin up a local testnet for your chain + +```shell +cd consumer + +# Starts 2 networks for the IBC testnet at http://127.0.0.1:8080. +# - Builds the docker image of your chain +# - Launches a testnet with IBC automatically connected and relayed +# +# Note: you can run a single node, non IBC testnet, with `make sh-testnet`. +make testnet +``` + +4. Open a new terminal window and send a transaction on your new chain + +```shell +# list the keys that have been provisioned with funds in genesis +consumerd keys list + +# send a transaction from one account to another +consumerd tx bank send acc0 $(consumerd keys show acc1 -a) 1337uconsu --chain-id=localchain-1 + +# enter "y" to confirm the transaction +# then query your balances tfor proof the transaction executed successfully +consumerd q bank balances $(consumerd keys show acc1 -a) +``` + +5. (optional) Send an IBC transaction + +```shell +# submit a cross chain transfer from acc0 to the other address +consumerd tx ibc-transfer transfer transfer channel-0 cosmos1hj5fveer5cjtn4wd6wstzugjfdxzl0xpxvjjvr 7uconsu --from=acc0 --chain-id=localchain-1 --yes + +# Query the other side to confirm it went through +sleep 10 + +# Interact with the other chain without having to install the cosmos binary +# - Endpoints found at: GET http://127.0.0.1:8080/info +local-ic interact localcosmos-1 query 'bank balances cosmos1hj5fveer5cjtn4wd6wstzugjfdxzl0xpxvjjvr' --api-endpoint=http://127.0.0.1:8080 +``` + +6. Push your new chain to a github repository + +```shell +# Create a new repository on GitHub from the gh cli +gh repo create ics-consumer --source=. --remote=origin --push +``` + +> You can also push it the old fashioned way with https://github.com/new and `git push origin main`. + +In this tutorial, we configured a new custom chain, launched a testnet for it, tested a simple token transfer, and confirmed it was successful. +This tutorial demonstrates just how easy it is to create a brand new custom Cosmos-SDK blockchain from scratch, saving developers time. + +## Modify your chain + +New module code is usually added in the `x/` directory of your repository. +Check out the [Cosmos SDK documentation](https://docs.cosmos.network/v0.50/build/building-modules/intro) for more information on how to add modules to your chain. + +Once you're ready you can preview your chain using the section below. + +## List your chain + +You can [list your chain on Forge](https://forge.cosmos.network/list-your-chain), even if it's not finished, in the **pre-launch** stage. diff --git a/docs/docs/consumer-development/offboarding.md b/docs/docs/consumer-development/offboarding.md index ff252ad550..0567566ed2 100644 --- a/docs/docs/consumer-development/offboarding.md +++ b/docs/docs/consumer-development/offboarding.md @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 5 title: Offboarding Checklist --- # Consumer Offboarding diff --git a/docs/docs/consumer-development/onboarding.md b/docs/docs/consumer-development/onboarding.md index 1613a2f268..5d6f42ef20 100644 --- a/docs/docs/consumer-development/onboarding.md +++ b/docs/docs/consumer-development/onboarding.md @@ -1,7 +1,8 @@ --- -sidebar_position: 3 +sidebar_position: 4 title: Onboarding Checklist --- + # Consumer Onboarding Checklist The following checklists will aid in onboarding a new consumer chain to Interchain Security. @@ -52,6 +53,7 @@ gather community support and accept feedback from the community, validators and - [ ] if desired, decide on power-shaping parameters (see [Power Shaping](../features/power-shaping.md)) Example of initialization parameters: + ```js // ConsumerInitializationParameters provided in MsgCreateConsumer or MsgUpdateConsumer { @@ -100,6 +102,7 @@ Example of initialization parameters: ``` Example of power-shaping parameters: + ```js // PowerShaping parameters provided in MsgCreateConsumer or MsgUpdateConsumer { From 956aede8d6ca93c88a7113c8fa6118fb7bc517c3 Mon Sep 17 00:00:00 2001 From: Jehan Date: Tue, 29 Oct 2024 12:35:05 -0700 Subject: [PATCH 04/11] docs: update spawn install instructions (#2379) update spawn install instructions --- .../consumer-development/create-with-spawn.md | 57 ++++++++++++++++--- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/docs/docs/consumer-development/create-with-spawn.md b/docs/docs/consumer-development/create-with-spawn.md index 07e202b041..752b784113 100644 --- a/docs/docs/consumer-development/create-with-spawn.md +++ b/docs/docs/consumer-development/create-with-spawn.md @@ -17,27 +17,66 @@ sidebar_position: 1 In this tutorial, we'll create and interact with a new Interchain security enabled blockchain called "consumer", with the token denomination "uconsu". -1. Clone this repo and install +1. Install Spawn ```shell -git clone https://github.com/rollchains/spawn.git +# Install from latest source +git clone https://github.com/rollchains/spawn.git --depth 1 --branch v0.50.10 + +# Change to this directory cd spawn -git checkout v0.50.4 + +# Clear Go modules cache for a fresh install +go clean -modcache + +# Install Spawn make install + +# Install Local Interchain (testnet runner) +make get-localic + +# Install docker container builder +make get-heighliner + +# Verify installations were successful +spawn + +local-ic + +heighliner + +# If you get "command 'spawn' not found", run the following +# Linux / Windows / Some MacOS +echo 'export PATH=$PATH:$(go env GOPATH)/bin' >> ~/.bashrc +source ~/.bashrc + +# MacOS +echo 'export PATH=$PATH:$(go env GOPATH)/bin' >> ~/.zshrc +source ~/.zshrc + +# Legacy MacOS Go +echo 'export PATH=$PATH:$HOME/go/bin' >> ~/.zshrc +source ~/.zshrc + +# Sometimes it can be good to also clear your cache +# especially WSL users +go clean -cache + + ``` 2. Create your chain using the `spawn` command and customize it to your needs! ```shell -GITHUB_USERNAME= +GITHUB_USERNAME= spawn new consumer \ --consensus=interchain-security \ ---bech32=consu `# the prefix for addresses` \ ---denom=uconsu `# the coin denomination to create` \ ---bin=consumerd `# the name of the binary` \ ---disabled=tokenfactory,globalfee,ibc-packetforward,ibc-ratelimit,cosmwasm,wasm-light-client,optimistic-execution,ignite-cli `# disable features. [tokenfactory,globalfee,ibc-packetforward,ibc-ratelimit,cosmwasm,wasm-light-client,ignite-cli]` \ ---org=${GITHUB_USERNAME} `# the github username or organization to use for the module imports, optional` +--bech32=consu \ +--denom=uconsu \ +--bin=consumerd \ +--disabled=tokenfactory,ibc-packetforward,ibc-ratelimit,cosmwasm,wasm-light-client \ +--org=${GITHUB_USERNAME} ``` > _NOTE:_ `spawn` creates a ready to use repository complete with `git` and GitHub CI. It can be quickly pushed to a new repository getting you and your team up and running quickly. From 399c833a515ce3be176f4e1cf7e7d2d2ecb66703 Mon Sep 17 00:00:00 2001 From: Sergey <83376337+freak12techno@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:49:15 +0300 Subject: [PATCH 05/11] fix: fix consumer chains list pagination (#2377) * chore: fix consumer chains list pagination * chore: added CHANGELOG entry * chore: review fixes --- .../2377-fix-list-consumer-chains-pagination.md | 2 ++ x/ccv/provider/keeper/grpc_query.go | 15 +++++++++------ x/ccv/provider/keeper/grpc_query_test.go | 13 ++++++++++++- 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 .changelog/unreleased/bug-fixes/2377-fix-list-consumer-chains-pagination.md diff --git a/.changelog/unreleased/bug-fixes/2377-fix-list-consumer-chains-pagination.md b/.changelog/unreleased/bug-fixes/2377-fix-list-consumer-chains-pagination.md new file mode 100644 index 0000000000..28623ee3e2 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/2377-fix-list-consumer-chains-pagination.md @@ -0,0 +1,2 @@ +- `[x/provider]` Fixed pagination in the list consumer chains query. + ([\#2377](https://github.com/cosmos/interchain-security/pull/2377)) \ No newline at end of file diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index dfc6b06501..2d0c1e1d10 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -56,23 +56,26 @@ func (k Keeper) QueryConsumerChains(goCtx context.Context, req *types.QueryConsu store := ctx.KVStore(k.storeKey) storePrefix := types.ConsumerIdToPhaseKeyPrefix() consumerPhaseStore := prefix.NewStore(store, []byte{storePrefix}) - pageRes, err := query.Paginate(consumerPhaseStore, req.Pagination, func(key, value []byte) error { + pageRes, err := query.FilteredPaginate(consumerPhaseStore, req.Pagination, func(key, value []byte, accumulate bool) (bool, error) { consumerId, err := types.ParseStringIdWithLenKey(storePrefix, append([]byte{storePrefix}, key...)) if err != nil { - return status.Error(codes.Internal, err.Error()) + return false, status.Error(codes.Internal, err.Error()) } phase := types.ConsumerPhase(binary.BigEndian.Uint32(value)) if req.Phase != types.CONSUMER_PHASE_UNSPECIFIED && req.Phase != phase { - return nil + return false, nil } c, err := k.GetConsumerChain(ctx, consumerId) if err != nil { - return status.Error(codes.Internal, err.Error()) + return false, status.Error(codes.Internal, err.Error()) } - chains = append(chains, &c) - return nil + + if accumulate { + chains = append(chains, &c) + } + return true, nil }) if err != nil { diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index 5f664a331a..fc9eebe43f 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -699,18 +699,21 @@ func TestQueryConsumerChains(t *testing.T) { setup func(ctx sdk.Context, pk keeper.Keeper) phase_filter types.ConsumerPhase limit uint64 + total uint64 expConsumers []*types.Chain }{ { name: "expect all consumers when phase filter isn't set", setup: func(ctx sdk.Context, pk keeper.Keeper) {}, expConsumers: consumers, + total: 4, }, { name: "expect an amount of consumer equal to the limit", setup: func(ctx sdk.Context, pk keeper.Keeper) {}, expConsumers: consumers[:3], limit: 3, + total: 4, }, { name: "expect registered consumers when phase filter is set to Registered", @@ -720,6 +723,7 @@ func TestQueryConsumerChains(t *testing.T) { }, phase_filter: types.CONSUMER_PHASE_REGISTERED, expConsumers: consumers[0:1], + total: 1, }, { name: "expect initialized consumers when phase is set to Initialized", @@ -729,6 +733,7 @@ func TestQueryConsumerChains(t *testing.T) { }, phase_filter: types.CONSUMER_PHASE_INITIALIZED, expConsumers: consumers[1:2], + total: 1, }, { name: "expect launched consumers when phase is set to Launched", @@ -738,6 +743,7 @@ func TestQueryConsumerChains(t *testing.T) { }, phase_filter: types.CONSUMER_PHASE_LAUNCHED, expConsumers: consumers[2:3], + total: 1, }, { name: "expect stopped consumers when phase is set to Stopped", @@ -747,6 +753,7 @@ func TestQueryConsumerChains(t *testing.T) { }, phase_filter: types.CONSUMER_PHASE_STOPPED, expConsumers: consumers[3:], + total: 1, }, } @@ -756,7 +763,8 @@ func TestQueryConsumerChains(t *testing.T) { req := types.QueryConsumerChainsRequest{ Phase: tc.phase_filter, Pagination: &sdkquery.PageRequest{ - Limit: tc.limit, + Limit: tc.limit, + CountTotal: true, }, } expectedResponse := types.QueryConsumerChainsResponse{ @@ -768,6 +776,9 @@ func TestQueryConsumerChains(t *testing.T) { if tc.limit != 0 { require.Len(t, res.GetChains(), int(tc.limit), tc.name) } + if tc.total != 0 { + require.Equal(t, res.Pagination.Total, tc.total, tc.name) + } }) } } From 802ed49d17bcbcec89324840298510c2c5dfa6df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:24:11 +0100 Subject: [PATCH 06/11] build(deps): bump actions/cache from 4.1.0 to 4.1.2 (#2376) Bumps [actions/cache](https://github.com/actions/cache) from 4.1.0 to 4.1.2. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 69d2448c8e..16ce734996 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,7 +39,7 @@ jobs: **/go.sum **/Makefile Makefile - - uses: actions/cache@v4.1.0 + - uses: actions/cache@v4.1.2 with: path: | ~/.cache/go-build From 6532a8818d0c3e287f04ae0cf3405a19b32e4090 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:24:29 +0100 Subject: [PATCH 07/11] build(deps): bump bufbuild/buf-setup-action from 1.45.0 to 1.46.0 (#2384) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.45.0 to 1.46.0. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.45.0...v1.46.0) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/proto-registry.yml | 2 +- .github/workflows/proto.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index 6850af4db0..d4714b611f 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.45.0 + - uses: bufbuild/buf-setup-action@v1.46.0 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index d7af43f29c..b1f3d2b2e7 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.45.0 + - uses: bufbuild/buf-setup-action@v1.46.0 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" From b3a2781457d20861578959ea39823465dbf01cc5 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 5 Nov 2024 15:16:25 +0100 Subject: [PATCH 08/11] test: fix key assignment in testing (#2344) fix key assignment in testing --- tests/integration/setup.go | 41 ----------------------- testutil/ibc_testing/generic_setup.go | 47 +++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/tests/integration/setup.go b/tests/integration/setup.go index f1f4faebbd..ca9adec496 100644 --- a/tests/integration/setup.go +++ b/tests/integration/setup.go @@ -9,20 +9,15 @@ import ( channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v8/testing" - "github.com/cosmos/ibc-go/v8/testing/mock" "github.com/stretchr/testify/suite" store "cosmossdk.io/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/cometbft/cometbft/abci/types" - tmencoding "github.com/cometbft/cometbft/crypto/encoding" icstestingutils "github.com/cosmos/interchain-security/v6/testutil/ibc_testing" testutil "github.com/cosmos/interchain-security/v6/testutil/integration" consumertypes "github.com/cosmos/interchain-security/v6/x/ccv/consumer/types" - providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v6/x/ccv/types" ) @@ -145,14 +140,6 @@ func (suite *CCVTestSuite) SetupTest() { params.BlocksPerEpoch = 10 providerKeeper.SetParams(suite.providerCtx(), params) - // re-assign all validator keys for the first consumer chain - // this has to be done before: - // 1. the consumer chain is added to the coordinator - // 2. MakeGenesis is called on the provider chain - // 3. ibc/testing sets the tendermint header for the consumer chain app - providerKeeper.SetConsumerPhase(suite.providerCtx(), icstestingutils.FirstConsumerID, providertypes.CONSUMER_PHASE_INITIALIZED) - preProposalKeyAssignment(suite, icstestingutils.FirstConsumerID) - // start consumer chains suite.consumerBundles = make(map[string]*icstestingutils.ConsumerBundle) for i := 0; i < icstestingutils.NumConsumers; i++ { @@ -416,34 +403,6 @@ func (s *CCVTestSuite) validateEndpointsClientConfig(consumerBundle icstestingut ) } -// preProposalKeyAssignment assigns keys to all provider validators for -// the consumer with consumerId before the chain is registered, i.e., -// before a client to the consumer is created -func preProposalKeyAssignment(s *CCVTestSuite, consumerId string) { - providerKeeper := s.providerApp.GetProviderKeeper() - - for _, val := range s.providerChain.Vals.Validators { - // get SDK validator - valAddr, err := sdk.ValAddressFromHex(val.Address.String()) - s.Require().NoError(err) - validator := s.getVal(s.providerCtx(), valAddr) - - // generate new PrivValidator - privVal := mock.NewPV() - tmPubKey, err := privVal.GetPubKey() - s.Require().NoError(err) - consumerKey, err := tmencoding.PubKeyToProto(tmPubKey) - s.Require().NoError(err) - - // add Signer to the provider chain as there is no consumer chain to add it; - // as a result, NewTestChainWithValSet in AddConsumer uses providerChain.Signers - s.providerChain.Signers[tmPubKey.Address().String()] = privVal - - err = providerKeeper.AssignConsumerKey(s.providerCtx(), consumerId, validator, consumerKey) - s.Require().NoError(err) - } -} - // packetSniffer implements the StreamingService interface. // Implements ListenEndBlock to record packets from events. type packetSniffer struct { diff --git a/testutil/ibc_testing/generic_setup.go b/testutil/ibc_testing/generic_setup.go index 680dc842b5..e208b5b29a 100644 --- a/testutil/ibc_testing/generic_setup.go +++ b/testutil/ibc_testing/generic_setup.go @@ -7,6 +7,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" ibctesting "github.com/cosmos/ibc-go/v8/testing" + "github.com/cosmos/ibc-go/v8/testing/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -19,6 +20,7 @@ import ( testutil "github.com/cosmos/interchain-security/v6/testutil/integration" testkeeper "github.com/cosmos/interchain-security/v6/testutil/keeper" consumerkeeper "github.com/cosmos/interchain-security/v6/x/ccv/consumer/keeper" + providerkeeper "github.com/cosmos/interchain-security/v6/x/ccv/provider/keeper" providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" ) @@ -155,9 +157,6 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( powerShapingParameters.Top_N = consumerTopNParams[index] // isn't used in CreateConsumerClient consumerId := providerKeeper.FetchAndIncrementConsumerId(providerChain.GetContext()) - if chainID == firstConsumerChainID { - FirstConsumerID = consumerId - } providerKeeper.SetConsumerChainId(providerChain.GetContext(), consumerId, chainID) err := providerKeeper.SetConsumerMetadata(providerChain.GetContext(), consumerId, consumerMetadata) s.Require().NoError(err) @@ -166,6 +165,15 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( err = providerKeeper.SetConsumerPowerShapingParameters(providerChain.GetContext(), consumerId, powerShapingParameters) s.Require().NoError(err) providerKeeper.SetConsumerPhase(providerChain.GetContext(), consumerId, providertypes.CONSUMER_PHASE_INITIALIZED) + if chainID == firstConsumerChainID { + FirstConsumerID = consumerId + // re-assign all validator keys for the first consumer chain + // this has to be done before: + // 1. the consumer chain is added to the coordinator + // 2. MakeGenesis is called on the provider chain + // 3. ibc/testing sets the tendermint header for the consumer chain app + preProposalKeyAssignment(s, *providerChain, providerKeeper, providerApp, FirstConsumerID) + } err = providerKeeper.AppendConsumerToBeLaunched(providerChain.GetContext(), consumerId, coordinator.CurrentTime) s.Require().NoError(err) @@ -228,3 +236,36 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( TopN: powerShapingParameters.Top_N, } } + +// preProposalKeyAssignment assigns keys to all provider validators for +// the consumer with consumerId before the chain is registered, i.e., +// before a client to the consumer is created +func preProposalKeyAssignment( + s *suite.Suite, + providerChain ibctesting.TestChain, + providerKeeper providerkeeper.Keeper, + providerApp testutil.ProviderApp, + consumerId string, +) { + for _, val := range providerChain.Vals.Validators { + // get SDK validator + valAddr, err := sdk.ValAddressFromHex(val.Address.String()) + s.Require().NoError(err) + validator, err := providerApp.GetTestStakingKeeper().GetValidator(providerChain.GetContext(), valAddr) + s.Require().NoError(err) + + // generate new PrivValidator + privVal := mock.NewPV() + tmPubKey, err := privVal.GetPubKey() + s.Require().NoError(err) + consumerKey, err := tmencoding.PubKeyToProto(tmPubKey) + s.Require().NoError(err) + + // add Signer to the provider chain as there is no consumer chain to add it; + // as a result, NewTestChainWithValSet in AddConsumer uses providerChain.Signers + providerChain.Signers[tmPubKey.Address().String()] = privVal + + err = providerKeeper.AssignConsumerKey(providerChain.GetContext(), consumerId, validator, consumerKey) + s.Require().NoError(err) + } +} From 00ba140fdd3ee99a303763c59ec56370d698e6ee Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 5 Nov 2024 16:11:38 +0100 Subject: [PATCH 09/11] feat: add consumer genesis time query (#2366) * add query handler for consumer genesis time * add consumer genesis time command to provider CLI * update docs * typo * typo #2 * typo #3 * add godoc * nit * add entry to changelog * fix tests after main merge * Update x/ccv/provider/keeper/grpc_query.go Co-authored-by: insumity --------- Co-authored-by: Marius Poke Co-authored-by: insumity --- .../features/2366-add-genesis-time-query.md | 3 + docs/docs/build/modules/02-provider.md | 75 +- .../ccv/provider/v1/query.proto | 17 + x/ccv/provider/client/cli/query.go | 28 + x/ccv/provider/keeper/grpc_query.go | 57 ++ x/ccv/provider/keeper/grpc_query_test.go | 133 +++- x/ccv/provider/types/query.pb.go | 694 ++++++++++++++---- x/ccv/provider/types/query.pb.gw.go | 101 +++ 8 files changed, 950 insertions(+), 158 deletions(-) create mode 100644 .changelog/unreleased/features/2366-add-genesis-time-query.md diff --git a/.changelog/unreleased/features/2366-add-genesis-time-query.md b/.changelog/unreleased/features/2366-add-genesis-time-query.md new file mode 100644 index 0000000000..5e4086f45b --- /dev/null +++ b/.changelog/unreleased/features/2366-add-genesis-time-query.md @@ -0,0 +1,3 @@ +- `[x/provider]` Add query for consumer genesis time, + which corresponds to creation time of consumer clients. +([\#2366](https://github.com/cosmos/interchain-security/pull/2366)) \ No newline at end of file diff --git a/docs/docs/build/modules/02-provider.md b/docs/docs/build/modules/02-provider.md index bbbaad0cf7..2b00d273c9 100644 --- a/docs/docs/build/modules/02-provider.md +++ b/docs/docs/build/modules/02-provider.md @@ -1566,6 +1566,29 @@ power_shaping_params: +##### Consumer Genesis Time + +The `consumer-genesis-time` command allows to query the genesis time of the consumer chain associated with the consumer id. + +```bash +interchain-security-pd query provider consumer-genesis-time [consumer-id] [flags] +``` + +
+ Example + +```bash +interchain-security-pd query provider consumer-genesis-time 0 +``` + +Output: + +```bash +genesis_time: "2024-10-18T08:13:23.507178095Z" +``` + +
+ #### Transactions The `tx` commands allows users to interact with the `provider` module. @@ -2409,7 +2432,7 @@ Output: #### Throttle State -The `QueryThrottleState` queries the main on-chain state relevant to slash packet throttling. +The `QueryThrottleState` endpoint queries the main on-chain state relevant to slash packet throttling. ```bash interchain_security.ccv.provider.v1.Query/QueryThrottleState @@ -2801,7 +2824,7 @@ Output: #### Consumer Chain -The `QueryConsumerChain` command allows to query the consumer chain associated with the consumer id. +The `QueryConsumerChain` endpoint allows to query the consumer chain associated with the consumer id. ```bash interchain_security.ccv.provider.v1.Query/QueryConsumerChain @@ -2850,6 +2873,29 @@ grpcurl -plaintext -d '{"consumer_id": "0"}' localhost:9090 interchain_security. +#### Consumer Genesis Time + +The `QueryConsumerGenesisTime` endpoint allows to query the genesis time of the consumer chain associated with the consumer id. + +```bash +interchain_security.ccv.provider.v1.Query/QueryConsumerGenesisTime +``` + +
+ Example + +```bash +grpcurl -plaintext -d '{"consumer_id": "0"}' localhost:9090 interchain_security.ccv.provider.v1.Query/QueryConsumerGenesisTime +``` + +```json +{ + "genesisTime": "2024-10-18T08:13:23.507178095Z" +} +``` + +
+ ### REST A user can query the `provider` module using REST endpoints. @@ -3524,3 +3570,28 @@ Output: ``` + +#### Consumer Genesis Time + +The `consumer_genesis_time` endpoint allows to query the genesis time of the consumer chain associated with the consumer id. + +```bash +interchain_security/ccv/provider/consumer_genesis_time/{consumer_id} +``` + +
+ Example + +```bash +curl http://localhost:1317/interchain_security/ccv/provider/consumer_genesis_time/0 +``` + +Output: + +```json +{ + "genesis_time":"2024-10-18T08:29:46.153234Z" +} +``` + +
diff --git a/proto/interchain_security/ccv/provider/v1/query.proto b/proto/interchain_security/ccv/provider/v1/query.proto index e7ce6002eb..e659b3cda6 100644 --- a/proto/interchain_security/ccv/provider/v1/query.proto +++ b/proto/interchain_security/ccv/provider/v1/query.proto @@ -144,6 +144,14 @@ service Query { option (google.api.http).get = "/interchain_security/ccv/provider/consumer_chain/{consumer_id}"; } + + // QueryConsumerGenesisTime returns the genesis time + // of the consumer chain associated with the provided consumer id + rpc QueryConsumerGenesisTime(QueryConsumerGenesisTimeRequest) + returns (QueryConsumerGenesisTimeResponse) { + option (google.api.http).get = + "/interchain_security/ccv/provider/consumer_genesis_time/{consumer_id}"; + } } message QueryConsumerGenesisRequest { @@ -390,3 +398,12 @@ message QueryConsumerChainResponse { ConsumerInitializationParameters init_params = 6; PowerShapingParameters power_shaping_params = 7; } + +message QueryConsumerGenesisTimeRequest { + string consumer_id = 1; +} + +message QueryConsumerGenesisTimeResponse { + google.protobuf.Timestamp genesis_time = 1 + [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; +} diff --git a/x/ccv/provider/client/cli/query.go b/x/ccv/provider/client/cli/query.go index 19d6d1f7fd..75fbbaf7a7 100644 --- a/x/ccv/provider/client/cli/query.go +++ b/x/ccv/provider/client/cli/query.go @@ -41,6 +41,7 @@ func NewQueryCmd() *cobra.Command { cmd.AddCommand(CmdBlocksUntilNextEpoch()) cmd.AddCommand(CmdConsumerIdFromClientId()) cmd.AddCommand(CmdConsumerChain()) + cmd.AddCommand(CmdConsumerGenesisTime()) return cmd } @@ -584,3 +585,30 @@ func CmdConsumerChain() *cobra.Command { return cmd } + +func CmdConsumerGenesisTime() *cobra.Command { + cmd := &cobra.Command{ + Use: "consumer-genesis-time [consumer-id]", + Short: "Query the genesis time of the consumer chain associated with the consumer id", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryConsumerGenesisTimeRequest{ConsumerId: args[0]} + res, err := queryClient.QueryConsumerGenesisTime(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index 2d0c1e1d10..d98f7e9b3e 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "fmt" "sort" + "time" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -627,3 +628,59 @@ func (k Keeper) QueryConsumerChain(goCtx context.Context, req *types.QueryConsum PowerShapingParams: &powerParams, }, nil } + +// QueryConsumerGenesisTime returns the genesis time +// of the consumer chain associated with the provided consumer id +func (k Keeper) QueryConsumerGenesisTime(goCtx context.Context, req *types.QueryConsumerGenesisTimeRequest) (*types.QueryConsumerGenesisTimeResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + consumerId := req.ConsumerId + if err := ccvtypes.ValidateConsumerId(consumerId); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + ctx := sdk.UnwrapSDKContext(goCtx) + + // Get consumer initialization params. If they aren't found, + // it means that there is no consumer for that consumerId. + params, err := k.GetConsumerInitializationParameters(ctx, consumerId) + if err != nil { + return nil, status.Errorf( + codes.InvalidArgument, + "cannot get consumer genesis time for consumer Id: %s: %s", + consumerId, types.ErrUnknownConsumerId, + ) + } + + // Get the consumer clientId. If it isn't found, it means + // that the consumer hasn't been launched or has been stopped and deleted. + clientID, ok := k.GetConsumerClientId(ctx, consumerId) + if !ok { + return nil, status.Errorf( + codes.InvalidArgument, + "cannot get consumer genesis time for consumer Id: %s: consumer hasn't been launched or has been stopped and deleted", + consumerId, + ) + } + + // Get the consensus state of the consumer client at the initial height, + // for which the timestamps corresponds to the consumer genesis time + cs, ok := k.clientKeeper.GetClientConsensusState( + ctx, + clientID, + params.InitialHeight, + ) + if !ok { + return nil, status.Errorf( + codes.InvalidArgument, + "cannot get consumer genesis time for consumer Id: %s: cannot find consensus state for initial height: %s", + consumerId, + params.InitialHeight, + ) + } + + return &types.QueryConsumerGenesisTimeResponse{ + GenesisTime: time.Unix(0, int64(cs.GetTimestamp())), + }, nil +} diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index fc9eebe43f..73f1f94ffb 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -6,18 +6,21 @@ import ( "sort" "strconv" "testing" + "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" "github.com/cometbft/cometbft/proto/tendermint/crypto" sdkquery "github.com/cosmos/cosmos-sdk/types/query" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" cryptotestutil "github.com/cosmos/interchain-security/v6/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v6/testutil/keeper" "github.com/cosmos/interchain-security/v6/x/ccv/provider/keeper" @@ -782,3 +785,131 @@ func TestQueryConsumerChains(t *testing.T) { }) } } + +func TestQueryConsumerGenesisTime(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + consumerId := "0" + chainId := "consumer-1" + initialHeight := clienttypes.Height{RevisionNumber: 1, RevisionHeight: 1} + clientId := "07-tendermint-0" + genesisTime := time.Now() + req := &types.QueryConsumerGenesisTimeRequest{ + ConsumerId: consumerId, + } + + testCases := []struct { + name string + setup func(ctx sdk.Context, pk keeper.Keeper) + req *types.QueryConsumerGenesisTimeRequest + expErr bool + expGenesisTime time.Time + }{ + { + name: "expect error when request is empty", + setup: func(ctx sdk.Context, pk keeper.Keeper) {}, + req: nil, + expErr: true, + }, + { + name: "expect error when consumer Id is invalid", + setup: func(ctx sdk.Context, pk keeper.Keeper) {}, + req: &types.QueryConsumerGenesisTimeRequest{ + ConsumerId: "invalidCID", + }, + expErr: true, + }, + { + name: "expect error when there is no consumer chain with this consumer id", + setup: func(ctx sdk.Context, pk keeper.Keeper) {}, + req: req, + expErr: true, + }, + { + name: "expect error when consumer hasn't been launched yet", + setup: func(ctx sdk.Context, pk keeper.Keeper) { + pk.SetConsumerChainId( + ctx, + consumerId, + chainId, + ) + err := pk.SetConsumerInitializationParameters( + ctx, + consumerId, + types.ConsumerInitializationParameters{ + InitialHeight: initialHeight, + }, + ) + require.NoError(t, err) + }, + req: req, + expErr: true, + }, + { + name: "expect error when consensus state cannot be found for consumer initial height", + setup: func(ctx sdk.Context, pk keeper.Keeper) { + pk.SetConsumerChainId( + ctx, + consumerId, + chainId, + ) + err := pk.SetConsumerInitializationParameters( + ctx, + consumerId, + types.ConsumerInitializationParameters{ + InitialHeight: initialHeight, + }, + ) + require.NoError(t, err) + + pk.SetConsumerClientId(ctx, consumerId, clientId) + mocks.MockClientKeeper.EXPECT().GetClientConsensusState(ctx, clientId, initialHeight).Return( + nil, false, + ) + }, + req: req, + expErr: true, + }, + { + name: "expect no error when there is a consensus state for the consumer initial height", + setup: func(ctx sdk.Context, pk keeper.Keeper) { + pk.SetConsumerChainId( + ctx, + consumerId, + chainId, + ) + err := pk.SetConsumerInitializationParameters( + ctx, + consumerId, + types.ConsumerInitializationParameters{ + InitialHeight: initialHeight, + }, + ) + require.NoError(t, err) + + pk.SetConsumerClientId(ctx, consumerId, clientId) + mocks.MockClientKeeper.EXPECT().GetClientConsensusState(ctx, clientId, initialHeight).Return( + ibcexported.ConsensusState(&ibctm.ConsensusState{Timestamp: genesisTime}), true, + ) + }, + req: req, + expErr: false, + expGenesisTime: genesisTime, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tc.setup(ctx, providerKeeper) + res, err := providerKeeper.QueryConsumerGenesisTime(ctx, tc.req) + if !tc.expErr { + require.NoError(t, err) + require.True(t, tc.expGenesisTime.Equal(res.GenesisTime)) + } else { + require.Error(t, err) + require.Equal(t, res, (*types.QueryConsumerGenesisTimeResponse)(nil)) + } + }) + } +} diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index f486a07b57..1fd93e436c 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -1828,6 +1828,94 @@ func (m *QueryConsumerChainResponse) GetPowerShapingParams() *PowerShapingParame return nil } +type QueryConsumerGenesisTimeRequest struct { + ConsumerId string `protobuf:"bytes,1,opt,name=consumer_id,json=consumerId,proto3" json:"consumer_id,omitempty"` +} + +func (m *QueryConsumerGenesisTimeRequest) Reset() { *m = QueryConsumerGenesisTimeRequest{} } +func (m *QueryConsumerGenesisTimeRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConsumerGenesisTimeRequest) ProtoMessage() {} +func (*QueryConsumerGenesisTimeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{33} +} +func (m *QueryConsumerGenesisTimeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerGenesisTimeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerGenesisTimeRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConsumerGenesisTimeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerGenesisTimeRequest.Merge(m, src) +} +func (m *QueryConsumerGenesisTimeRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerGenesisTimeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerGenesisTimeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerGenesisTimeRequest proto.InternalMessageInfo + +func (m *QueryConsumerGenesisTimeRequest) GetConsumerId() string { + if m != nil { + return m.ConsumerId + } + return "" +} + +type QueryConsumerGenesisTimeResponse struct { + GenesisTime time.Time `protobuf:"bytes,1,opt,name=genesis_time,json=genesisTime,proto3,stdtime" json:"genesis_time"` +} + +func (m *QueryConsumerGenesisTimeResponse) Reset() { *m = QueryConsumerGenesisTimeResponse{} } +func (m *QueryConsumerGenesisTimeResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConsumerGenesisTimeResponse) ProtoMessage() {} +func (*QueryConsumerGenesisTimeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{34} +} +func (m *QueryConsumerGenesisTimeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerGenesisTimeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerGenesisTimeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConsumerGenesisTimeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerGenesisTimeResponse.Merge(m, src) +} +func (m *QueryConsumerGenesisTimeResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerGenesisTimeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerGenesisTimeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerGenesisTimeResponse proto.InternalMessageInfo + +func (m *QueryConsumerGenesisTimeResponse) GetGenesisTime() time.Time { + if m != nil { + return m.GenesisTime + } + return time.Time{} +} + func init() { proto.RegisterType((*QueryConsumerGenesisRequest)(nil), "interchain_security.ccv.provider.v1.QueryConsumerGenesisRequest") proto.RegisterType((*QueryConsumerGenesisResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerGenesisResponse") @@ -1862,6 +1950,8 @@ func init() { proto.RegisterType((*QueryConsumerIdFromClientIdResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerIdFromClientIdResponse") proto.RegisterType((*QueryConsumerChainRequest)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainRequest") proto.RegisterType((*QueryConsumerChainResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainResponse") + proto.RegisterType((*QueryConsumerGenesisTimeRequest)(nil), "interchain_security.ccv.provider.v1.QueryConsumerGenesisTimeRequest") + proto.RegisterType((*QueryConsumerGenesisTimeResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerGenesisTimeResponse") } func init() { @@ -1869,162 +1959,166 @@ func init() { } var fileDescriptor_422512d7b7586cd7 = []byte{ - // 2465 bytes of a gzipped FileDescriptorProto + // 2532 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x5a, 0xcd, 0x6f, 0xdc, 0xc6, - 0x15, 0x17, 0x57, 0x1f, 0x5e, 0x8d, 0x2c, 0x39, 0x1e, 0xcb, 0xd6, 0x7a, 0x65, 0x6b, 0x65, 0x3a, - 0x6e, 0x15, 0x39, 0x5e, 0x4a, 0x2a, 0xf2, 0xe5, 0xc4, 0x1f, 0x5a, 0x59, 0xb2, 0x05, 0xc7, 0xb6, - 0x42, 0x29, 0x0e, 0xe0, 0xd4, 0x65, 0x47, 0xe4, 0x64, 0x35, 0x15, 0x97, 0xa4, 0x39, 0xa3, 0xb5, - 0xb7, 0x86, 0x2f, 0x3d, 0xe5, 0xd0, 0x02, 0x09, 0x82, 0x9e, 0x9b, 0x73, 0x0f, 0x45, 0x51, 0x04, - 0xfd, 0x07, 0x7a, 0xc9, 0xad, 0x69, 0x7a, 0x29, 0x5a, 0xd4, 0x6d, 0xed, 0x16, 0xe8, 0xa5, 0x87, - 0xa6, 0xfd, 0x03, 0x82, 0x19, 0x0e, 0xb9, 0x4b, 0x9a, 0xd2, 0x92, 0x5a, 0xdd, 0xc4, 0x99, 0xf7, - 0x7e, 0xf3, 0xde, 0x9b, 0xf7, 0xde, 0xbc, 0xf7, 0x56, 0x40, 0x23, 0x0e, 0xc3, 0xbe, 0xb9, 0x85, - 0x88, 0x63, 0x50, 0x6c, 0xee, 0xf8, 0x84, 0xb5, 0x34, 0xd3, 0x6c, 0x6a, 0x9e, 0xef, 0x36, 0x89, - 0x85, 0x7d, 0xad, 0x39, 0xaf, 0x3d, 0xd8, 0xc1, 0x7e, 0xab, 0xea, 0xf9, 0x2e, 0x73, 0xe1, 0xd9, - 0x14, 0x86, 0xaa, 0x69, 0x36, 0xab, 0x21, 0x43, 0xb5, 0x39, 0x5f, 0x3e, 0x55, 0x77, 0xdd, 0xba, - 0x8d, 0x35, 0xe4, 0x11, 0x0d, 0x39, 0x8e, 0xcb, 0x10, 0x23, 0xae, 0x43, 0x03, 0x88, 0xf2, 0x78, - 0xdd, 0xad, 0xbb, 0xe2, 0x4f, 0x8d, 0xff, 0x25, 0x57, 0x2b, 0x92, 0x47, 0x7c, 0x6d, 0xee, 0x7c, - 0xa4, 0x31, 0xd2, 0xc0, 0x94, 0xa1, 0x86, 0x27, 0x09, 0x16, 0xb2, 0x88, 0x1a, 0x49, 0x11, 0xf0, - 0xcc, 0xed, 0xc6, 0xd3, 0x9c, 0xd7, 0xe8, 0x16, 0xf2, 0xb1, 0x65, 0x98, 0xae, 0x43, 0x77, 0x1a, - 0x11, 0xc7, 0xb9, 0x3d, 0x38, 0x1e, 0x12, 0x1f, 0x4b, 0xb2, 0x53, 0x0c, 0x3b, 0x16, 0xf6, 0x1b, - 0xc4, 0x61, 0x9a, 0xe9, 0xb7, 0x3c, 0xe6, 0x6a, 0xdb, 0xb8, 0x15, 0x6a, 0x78, 0xd2, 0x74, 0x69, - 0xc3, 0xa5, 0x46, 0xa0, 0x64, 0xf0, 0x21, 0xb7, 0x5e, 0x0e, 0xbe, 0x34, 0xca, 0xd0, 0x36, 0x71, - 0xea, 0x5a, 0x73, 0x7e, 0x13, 0x33, 0x34, 0x1f, 0x7e, 0x4b, 0xaa, 0x59, 0x49, 0xb5, 0x89, 0x28, - 0x0e, 0xcc, 0x1f, 0x11, 0x7a, 0xa8, 0x4e, 0x1c, 0x61, 0xcf, 0x80, 0x56, 0xbd, 0x0c, 0x26, 0xdf, - 0xe3, 0x14, 0x4b, 0x52, 0x91, 0xeb, 0xd8, 0xc1, 0x94, 0x50, 0x1d, 0x3f, 0xd8, 0xc1, 0x94, 0xc1, - 0x0a, 0x18, 0x09, 0x55, 0x34, 0x88, 0x55, 0x52, 0xa6, 0x95, 0x99, 0x61, 0x1d, 0x84, 0x4b, 0xab, - 0x96, 0xfa, 0x18, 0x9c, 0x4a, 0xe7, 0xa7, 0x9e, 0xeb, 0x50, 0x0c, 0x3f, 0x04, 0xa3, 0xf5, 0x60, - 0xc9, 0xa0, 0x0c, 0x31, 0x2c, 0x20, 0x46, 0x16, 0xe6, 0xaa, 0xbb, 0x79, 0x42, 0x73, 0xbe, 0x9a, - 0xc0, 0x5a, 0xe7, 0x7c, 0xb5, 0x81, 0x2f, 0x9f, 0x56, 0xfa, 0xf4, 0xc3, 0xf5, 0x8e, 0x35, 0xf5, - 0x57, 0x0a, 0x28, 0xc7, 0x4e, 0x5f, 0xe2, 0x78, 0x91, 0xf0, 0x37, 0xc0, 0xa0, 0xb7, 0x85, 0x68, - 0x70, 0xe6, 0xd8, 0xc2, 0x42, 0x35, 0x83, 0xf7, 0x45, 0x87, 0xaf, 0x71, 0x4e, 0x3d, 0x00, 0x80, - 0x2b, 0x00, 0xb4, 0x2d, 0x57, 0x2a, 0x08, 0x15, 0xbe, 0x53, 0x95, 0x57, 0xc3, 0xcd, 0x5c, 0x0d, - 0xbc, 0x5c, 0x9a, 0xb9, 0xba, 0x86, 0xea, 0x58, 0x4a, 0xa1, 0x77, 0x70, 0xaa, 0xbf, 0x54, 0x12, - 0xe6, 0x0e, 0x05, 0x96, 0xd6, 0xaa, 0x81, 0x21, 0x21, 0x1e, 0x2d, 0x29, 0xd3, 0xfd, 0x33, 0x23, - 0x0b, 0xb3, 0xd9, 0x44, 0xe6, 0xdb, 0xba, 0xe4, 0x84, 0xd7, 0x53, 0x64, 0xfd, 0x6e, 0x57, 0x59, - 0x03, 0x01, 0x62, 0xc2, 0x7e, 0x36, 0x08, 0x06, 0x05, 0x34, 0x3c, 0x09, 0x8a, 0x81, 0x08, 0x91, - 0x0b, 0x1c, 0x12, 0xdf, 0xab, 0x16, 0x9c, 0x04, 0xc3, 0xa6, 0x4d, 0xb0, 0xc3, 0xf8, 0x5e, 0x41, - 0xec, 0x15, 0x83, 0x85, 0x55, 0x0b, 0x1e, 0x03, 0x83, 0xcc, 0xf5, 0x8c, 0xdb, 0xa5, 0xfe, 0x69, - 0x65, 0x66, 0x54, 0x1f, 0x60, 0xae, 0x77, 0x1b, 0xce, 0x02, 0xd8, 0x20, 0x8e, 0xe1, 0xb9, 0x0f, - 0xb9, 0x4f, 0x39, 0x46, 0x40, 0x31, 0x30, 0xad, 0xcc, 0xf4, 0xeb, 0x63, 0x0d, 0xe2, 0xac, 0xf1, - 0x8d, 0x55, 0x67, 0x83, 0xd3, 0xce, 0x81, 0xf1, 0x26, 0xb2, 0x89, 0x85, 0x98, 0xeb, 0x53, 0xc9, - 0x62, 0x22, 0xaf, 0x34, 0x28, 0xf0, 0x60, 0x7b, 0x4f, 0x30, 0x2d, 0x21, 0x0f, 0xce, 0x82, 0xa3, - 0xd1, 0xaa, 0x41, 0x31, 0x13, 0xe4, 0x43, 0x82, 0xfc, 0x48, 0xb4, 0xb1, 0x8e, 0x19, 0xa7, 0x3d, - 0x05, 0x86, 0x91, 0x6d, 0xbb, 0x0f, 0x6d, 0x42, 0x59, 0xe9, 0xd0, 0x74, 0xff, 0xcc, 0xb0, 0xde, - 0x5e, 0x80, 0x65, 0x50, 0xb4, 0xb0, 0xd3, 0x12, 0x9b, 0x45, 0xb1, 0x19, 0x7d, 0xc3, 0xf1, 0xd0, - 0xb3, 0x86, 0x85, 0xc6, 0xd2, 0x4b, 0x3e, 0x00, 0xc5, 0x06, 0x66, 0xc8, 0x42, 0x0c, 0x95, 0x80, - 0xb0, 0xfb, 0x6b, 0xb9, 0x5c, 0xee, 0x96, 0x64, 0x96, 0xbe, 0x1e, 0x81, 0x71, 0x23, 0x73, 0x93, - 0xf1, 0x28, 0xc7, 0xa5, 0x91, 0x69, 0x65, 0x66, 0x40, 0x2f, 0x36, 0x88, 0xb3, 0xce, 0xbf, 0x61, - 0x15, 0x1c, 0x13, 0x42, 0x1b, 0xc4, 0x41, 0x26, 0x23, 0x4d, 0x6c, 0x34, 0x91, 0x4d, 0x4b, 0x87, - 0xa7, 0x95, 0x99, 0xa2, 0x7e, 0x54, 0x6c, 0xad, 0xca, 0x9d, 0xbb, 0xc8, 0xa6, 0xc9, 0x90, 0x1e, - 0x4d, 0x86, 0x34, 0x7c, 0x04, 0x4e, 0x46, 0x56, 0xc0, 0x96, 0xe1, 0xe3, 0x87, 0xc8, 0xb7, 0x0c, - 0x0b, 0x3b, 0x6e, 0x83, 0x96, 0xc6, 0x84, 0x5e, 0xef, 0x64, 0xd2, 0x6b, 0xb1, 0x8d, 0xa2, 0x0b, - 0x90, 0x6b, 0x02, 0x43, 0x9f, 0x40, 0xe9, 0x1b, 0x50, 0x05, 0x87, 0x3d, 0x9f, 0xb8, 0x1c, 0x4c, - 0x98, 0xfd, 0x88, 0x30, 0x7b, 0x6c, 0x4d, 0xfd, 0x99, 0x02, 0xce, 0x88, 0x10, 0xba, 0x1b, 0xde, - 0x66, 0x68, 0xbe, 0x45, 0xcb, 0xf2, 0xc3, 0xd0, 0xbf, 0x04, 0x5e, 0x0a, 0x25, 0x31, 0x90, 0x65, - 0xf9, 0x98, 0xd2, 0xc0, 0x73, 0x6b, 0xf0, 0x9b, 0xa7, 0x95, 0xb1, 0x16, 0x6a, 0xd8, 0x17, 0x55, - 0xb9, 0xa1, 0xea, 0x47, 0x42, 0xda, 0xc5, 0x60, 0x25, 0x69, 0xa3, 0x42, 0xd2, 0x46, 0x17, 0x8b, - 0x1f, 0x7f, 0x5e, 0xe9, 0xfb, 0xf7, 0xe7, 0x95, 0x3e, 0xf5, 0x0e, 0x50, 0xf7, 0x12, 0x47, 0x06, - 0xf6, 0x2b, 0xe0, 0xa5, 0x08, 0x30, 0x26, 0x8f, 0x7e, 0xc4, 0xec, 0xa0, 0xe7, 0xd2, 0xbc, 0xa8, - 0xe0, 0x5a, 0x87, 0x74, 0x1d, 0x0a, 0xa6, 0x03, 0xa6, 0x2b, 0x98, 0x38, 0xa4, 0x27, 0x05, 0xe3, - 0xe2, 0xb4, 0x15, 0x4c, 0x37, 0xf8, 0x0b, 0xc6, 0x55, 0x27, 0xc1, 0x49, 0x01, 0xb8, 0xb1, 0xe5, - 0xbb, 0x8c, 0xd9, 0x58, 0xe4, 0x72, 0xa9, 0x97, 0xfa, 0x87, 0x30, 0xa5, 0x27, 0x76, 0xe5, 0x31, - 0x15, 0x30, 0x42, 0x6d, 0x44, 0xb7, 0x8c, 0x06, 0x66, 0xd8, 0x17, 0x27, 0xf4, 0xeb, 0x40, 0x2c, - 0xdd, 0xe2, 0x2b, 0x70, 0x01, 0x1c, 0xef, 0x20, 0x30, 0x84, 0xa7, 0x21, 0xc7, 0xc4, 0x42, 0xc5, - 0x7e, 0xfd, 0x58, 0x9b, 0x74, 0x31, 0xdc, 0x82, 0x3f, 0x00, 0x25, 0x07, 0x3f, 0x62, 0x86, 0x8f, - 0x3d, 0x1b, 0x3b, 0x84, 0x6e, 0x19, 0x26, 0x72, 0x2c, 0xae, 0x2c, 0x16, 0x99, 0x6b, 0x64, 0xa1, - 0x5c, 0x0d, 0xea, 0x8b, 0x6a, 0x58, 0x5f, 0x54, 0x37, 0xc2, 0xfa, 0xa2, 0x56, 0xe4, 0xc1, 0xfa, - 0xc9, 0xdf, 0x2a, 0x8a, 0x7e, 0x82, 0xa3, 0xe8, 0x21, 0xc8, 0x52, 0x88, 0xa1, 0xbe, 0x0a, 0x66, - 0x85, 0x4a, 0x3a, 0xae, 0x73, 0x9f, 0xf7, 0xb1, 0x15, 0xfa, 0x48, 0x2c, 0x2c, 0xa4, 0x05, 0x96, - 0xc1, 0xf9, 0x4c, 0xd4, 0xd2, 0x22, 0x27, 0xc0, 0x90, 0x0c, 0x4d, 0x45, 0x44, 0x8b, 0xfc, 0x52, - 0xdf, 0x05, 0xaf, 0x08, 0x98, 0x45, 0xdb, 0x5e, 0x43, 0xc4, 0xa7, 0x77, 0x91, 0xcd, 0x71, 0xf8, - 0x25, 0xd4, 0x5a, 0x6d, 0xc4, 0x8c, 0xcf, 0xfc, 0x2f, 0x14, 0xa9, 0x43, 0x17, 0x38, 0x29, 0xd4, - 0x03, 0x70, 0xd4, 0x43, 0xc4, 0xe7, 0x99, 0x88, 0x97, 0x48, 0xc2, 0x23, 0xe4, 0x93, 0xb6, 0x92, - 0x29, 0x75, 0xf0, 0x33, 0x82, 0x23, 0xf8, 0x09, 0x91, 0xc7, 0x39, 0x6d, 0x5b, 0x8c, 0x79, 0x31, - 0x12, 0xf5, 0xff, 0x0a, 0x38, 0xd3, 0x95, 0x0b, 0xae, 0xec, 0x9a, 0x17, 0x26, 0xbf, 0x79, 0x5a, - 0x99, 0x08, 0xc2, 0x26, 0x49, 0x91, 0x92, 0x20, 0x56, 0x52, 0xc2, 0xaf, 0x90, 0xc4, 0x49, 0x52, - 0xa4, 0xc4, 0xe1, 0x15, 0x70, 0x38, 0xa2, 0xda, 0xc6, 0x2d, 0xe9, 0x6e, 0xa7, 0xaa, 0xed, 0x02, - 0xb1, 0x1a, 0x14, 0x88, 0xd5, 0xb5, 0x9d, 0x4d, 0x9b, 0x98, 0x37, 0x71, 0x4b, 0x8f, 0xae, 0xea, - 0x26, 0x6e, 0xa9, 0xe3, 0x00, 0x8a, 0x7b, 0x59, 0x43, 0x3e, 0x6a, 0xfb, 0xd0, 0x0f, 0xc1, 0xb1, - 0xd8, 0xaa, 0xbc, 0x96, 0x55, 0x30, 0xe4, 0x89, 0x15, 0x59, 0x85, 0x9d, 0xcf, 0x78, 0x17, 0x9c, - 0x45, 0x3e, 0x4a, 0x12, 0x40, 0xbd, 0x25, 0xfd, 0x21, 0x56, 0xc8, 0xdc, 0xf1, 0x18, 0xb6, 0x56, - 0x9d, 0x28, 0x53, 0x64, 0x2f, 0x23, 0x1f, 0x48, 0xa7, 0xef, 0x06, 0x17, 0xd5, 0x49, 0xa7, 0x3b, - 0xeb, 0x82, 0xc4, 0x7d, 0xe1, 0x30, 0x16, 0x26, 0x3b, 0x0a, 0x84, 0xf8, 0x05, 0x62, 0xaa, 0x2e, - 0x82, 0xa9, 0xd8, 0x91, 0xfb, 0x90, 0xfa, 0xd3, 0x43, 0x60, 0x7a, 0x17, 0x8c, 0xe8, 0xaf, 0x5e, - 0x9f, 0xa2, 0xa4, 0x87, 0x14, 0x72, 0x7a, 0x08, 0x2c, 0x81, 0x41, 0x51, 0x38, 0x09, 0xdf, 0xea, - 0xaf, 0x15, 0x4a, 0x8a, 0x1e, 0x2c, 0xc0, 0xb7, 0xc0, 0x80, 0xcf, 0x73, 0xdc, 0x80, 0x90, 0xe6, - 0x1c, 0xbf, 0xdf, 0x3f, 0x3f, 0xad, 0x4c, 0x06, 0xa5, 0x22, 0xb5, 0xb6, 0xab, 0xc4, 0xd5, 0x1a, - 0x88, 0x6d, 0x55, 0xdf, 0xc5, 0x75, 0x64, 0xb6, 0xae, 0x61, 0xb3, 0xa4, 0xe8, 0x82, 0x05, 0x9e, - 0x03, 0x63, 0x91, 0x54, 0x01, 0xfa, 0xa0, 0xc8, 0xaf, 0xa3, 0xe1, 0xaa, 0x28, 0xc8, 0xe0, 0x7d, - 0x50, 0x8a, 0xc8, 0x4c, 0xb7, 0xd1, 0x20, 0x94, 0x12, 0xd7, 0x31, 0xc4, 0xa9, 0x43, 0xe2, 0xd4, - 0xb3, 0x19, 0x4e, 0xd5, 0x4f, 0x84, 0x20, 0x4b, 0x11, 0x86, 0xce, 0xa5, 0xb8, 0x0f, 0x4a, 0x91, - 0x69, 0x93, 0xf0, 0x87, 0x72, 0xc0, 0x87, 0x20, 0x09, 0xf8, 0x9b, 0x60, 0xc4, 0xc2, 0xd4, 0xf4, - 0x89, 0x27, 0x4a, 0xe9, 0xa2, 0xb0, 0xfc, 0xd9, 0xb0, 0x94, 0x0e, 0x7b, 0xae, 0xb0, 0x8e, 0xbe, - 0xd6, 0x26, 0x95, 0xb1, 0xd2, 0xc9, 0x0d, 0xef, 0x83, 0x93, 0x91, 0xac, 0xae, 0x87, 0x7d, 0x51, - 0xa0, 0x86, 0xfe, 0x20, 0xca, 0xc8, 0xda, 0x99, 0xaf, 0xbf, 0xb8, 0x70, 0x5a, 0xa2, 0x47, 0xfe, - 0x23, 0xfd, 0x60, 0x9d, 0xf9, 0xc4, 0xa9, 0xeb, 0x13, 0x21, 0xc6, 0x1d, 0x09, 0x11, 0xba, 0xc9, - 0x09, 0x30, 0xf4, 0x23, 0x44, 0x6c, 0x6c, 0x89, 0xca, 0xb3, 0xa8, 0xcb, 0x2f, 0x78, 0x11, 0x0c, - 0xf1, 0xbe, 0x6b, 0x87, 0x8a, 0xba, 0x71, 0x6c, 0x41, 0xdd, 0x4d, 0xfc, 0x9a, 0xeb, 0x58, 0xeb, - 0x82, 0x52, 0x97, 0x1c, 0x70, 0x03, 0x44, 0xde, 0x68, 0x30, 0x77, 0x1b, 0x3b, 0x41, 0x55, 0x39, - 0x5c, 0x3b, 0x2f, 0xad, 0x7a, 0xfc, 0x45, 0xab, 0xae, 0x3a, 0xec, 0xeb, 0x2f, 0x2e, 0x00, 0x79, - 0xc8, 0xaa, 0xc3, 0xf4, 0xb1, 0x10, 0x63, 0x43, 0x40, 0x70, 0xd7, 0x89, 0x50, 0x03, 0xd7, 0x19, - 0x0d, 0x5c, 0x27, 0x5c, 0x0d, 0x5c, 0xe7, 0x75, 0x30, 0x21, 0xa3, 0x17, 0x53, 0xc3, 0xdc, 0xf1, - 0x7d, 0xde, 0x63, 0x60, 0xcf, 0x35, 0xb7, 0x44, 0x0d, 0x5a, 0xd4, 0x8f, 0x47, 0xdb, 0x4b, 0xc1, - 0xee, 0x32, 0xdf, 0x54, 0x3f, 0x56, 0x40, 0x65, 0xd7, 0xb8, 0x96, 0xe9, 0x03, 0x03, 0xd0, 0xce, - 0x0c, 0xf2, 0x5d, 0x5a, 0xce, 0x94, 0x0b, 0xbb, 0x45, 0xbb, 0xde, 0x01, 0xac, 0x3e, 0x00, 0x73, - 0x29, 0xcd, 0x5e, 0x44, 0x7b, 0x03, 0xd1, 0x0d, 0x57, 0x7e, 0xe1, 0x83, 0x29, 0x5c, 0xd5, 0xbb, - 0x60, 0x3e, 0xc7, 0x91, 0xd2, 0x1c, 0x67, 0x3a, 0x52, 0x0c, 0xb1, 0xc2, 0xe4, 0x39, 0xd2, 0x4e, - 0x74, 0xa2, 0x28, 0x3d, 0x9f, 0x5e, 0xe6, 0xc6, 0x63, 0x26, 0x6b, 0xea, 0x4c, 0xd5, 0xb3, 0x90, - 0x5d, 0xcf, 0x3a, 0x78, 0x35, 0x9b, 0x38, 0x52, 0xc5, 0x37, 0x64, 0xaa, 0x53, 0xb2, 0x67, 0x05, - 0xc1, 0xa0, 0xaa, 0x32, 0xc3, 0xd7, 0x6c, 0xd7, 0xdc, 0xa6, 0xef, 0x3b, 0x8c, 0xd8, 0xb7, 0xf1, - 0xa3, 0xc0, 0xd7, 0xc2, 0xd7, 0xf6, 0x9e, 0x2c, 0xd8, 0xd3, 0x69, 0xa4, 0x04, 0xaf, 0x81, 0x89, - 0x4d, 0xb1, 0x6f, 0xec, 0x70, 0x02, 0x43, 0x54, 0x9c, 0x81, 0x3f, 0x2b, 0xa2, 0xa3, 0x1b, 0xdf, - 0x4c, 0x61, 0x57, 0x17, 0x65, 0xf5, 0xbd, 0x14, 0x99, 0x6e, 0xc5, 0x77, 0x1b, 0x4b, 0xb2, 0xc3, - 0x0e, 0xcd, 0x1d, 0xeb, 0xc2, 0x95, 0x78, 0x17, 0xae, 0xae, 0x80, 0xb3, 0x7b, 0x42, 0xb4, 0x4b, - 0xeb, 0xbd, 0x5f, 0xbb, 0x77, 0x64, 0xdd, 0x1e, 0xf3, 0xad, 0xcc, 0x6f, 0xe5, 0xef, 0xfa, 0xd3, - 0x66, 0x35, 0x99, 0x4f, 0x8f, 0xcd, 0x20, 0x0a, 0xf1, 0x19, 0xc4, 0x59, 0x30, 0xea, 0x3e, 0x74, - 0x3a, 0x1c, 0xa9, 0x5f, 0xec, 0x1f, 0x16, 0x8b, 0x61, 0x82, 0x8c, 0x5a, 0xf6, 0x81, 0xdd, 0x5a, - 0xf6, 0xc1, 0x83, 0x6c, 0xd9, 0x3f, 0x02, 0x23, 0xc4, 0x21, 0xcc, 0x90, 0xf5, 0xd6, 0x90, 0xc0, - 0x5e, 0xce, 0x85, 0xbd, 0xea, 0x10, 0x46, 0x90, 0x4d, 0x7e, 0x2c, 0xc6, 0x31, 0xa2, 0x0a, 0xe3, - 0x7d, 0x0b, 0xd5, 0x01, 0x47, 0x0e, 0xaa, 0x32, 0xd8, 0x00, 0xe3, 0xc1, 0x58, 0x84, 0x6e, 0x21, - 0x8f, 0x38, 0xf5, 0xf0, 0xc0, 0x43, 0xe2, 0xc0, 0xb7, 0xb3, 0x15, 0x78, 0x1c, 0x60, 0x3d, 0xe0, - 0xef, 0x38, 0x06, 0x7a, 0xc9, 0x75, 0xba, 0xf0, 0x8f, 0xd3, 0x60, 0x50, 0xdc, 0x22, 0xfc, 0x97, - 0x02, 0xc6, 0xd3, 0x26, 0x7f, 0xf0, 0x6a, 0xfe, 0x44, 0x1a, 0x1f, 0x3a, 0x96, 0x17, 0x7b, 0x40, - 0x08, 0xdc, 0x49, 0xbd, 0xf1, 0x93, 0x3f, 0xfe, 0xf3, 0xb3, 0x42, 0x0d, 0x5e, 0xed, 0x3e, 0xa2, - 0x8e, 0xdc, 0x4e, 0x8e, 0x16, 0xb5, 0xc7, 0x1d, 0x8e, 0xf8, 0x04, 0xfe, 0x45, 0x91, 0xb5, 0x74, - 0x3c, 0xa5, 0xc2, 0x2b, 0xf9, 0x85, 0x8c, 0x4d, 0x27, 0xcb, 0x57, 0xf7, 0x0f, 0x20, 0x95, 0x5c, - 0x14, 0x4a, 0xbe, 0x0d, 0xdf, 0xca, 0xa1, 0x64, 0x30, 0x24, 0xd4, 0x1e, 0x0b, 0xf7, 0x7f, 0x02, - 0x3f, 0x2d, 0xc8, 0xa8, 0x4c, 0x1d, 0x5f, 0xc0, 0x95, 0xec, 0x32, 0xee, 0x35, 0x8e, 0x29, 0x5f, - 0xef, 0x19, 0x47, 0xaa, 0xbc, 0x29, 0x54, 0xfe, 0x3e, 0xbc, 0x97, 0xe1, 0xa7, 0x87, 0x68, 0x0c, - 0x18, 0xeb, 0xc3, 0xe2, 0xd7, 0xab, 0x3d, 0x4e, 0xbe, 0x42, 0x69, 0x36, 0xe9, 0x6c, 0x1e, 0xf6, - 0x65, 0x93, 0x94, 0x09, 0xce, 0xbe, 0x6c, 0x92, 0x36, 0x7a, 0xd9, 0x9f, 0x4d, 0x62, 0x6a, 0x27, - 0x6d, 0x92, 0x6c, 0x5c, 0x9f, 0xc0, 0xdf, 0x2b, 0xb2, 0xcf, 0x8c, 0x8d, 0x65, 0xe0, 0xe5, 0xec, - 0x3a, 0xa4, 0x4d, 0x7b, 0xca, 0x57, 0xf6, 0xcd, 0x2f, 0x75, 0x7f, 0x53, 0xe8, 0xbe, 0x00, 0xe7, - 0xba, 0xeb, 0xce, 0x24, 0x40, 0xf0, 0x3b, 0x04, 0xfc, 0x79, 0x41, 0x3e, 0x8b, 0x7b, 0xcf, 0x59, - 0xe0, 0x9d, 0xec, 0x22, 0x66, 0x9a, 0xef, 0x94, 0xd7, 0x0e, 0x0e, 0x50, 0x1a, 0xe1, 0xa6, 0x30, - 0xc2, 0x32, 0x5c, 0xea, 0x6e, 0x04, 0x3f, 0x42, 0x6c, 0x47, 0x45, 0x6c, 0xc0, 0x0b, 0x7f, 0x5a, - 0x90, 0x15, 0xc7, 0x9e, 0x93, 0x1e, 0x78, 0x3b, 0xbb, 0x16, 0x59, 0x26, 0x50, 0xe5, 0x3b, 0x07, - 0x86, 0x27, 0x8d, 0xb2, 0x2c, 0x8c, 0x72, 0x05, 0x5e, 0xea, 0x6e, 0x14, 0xe9, 0xe5, 0x86, 0xc7, - 0x51, 0x13, 0xe9, 0xff, 0x37, 0x0a, 0x18, 0xe9, 0x18, 0xa5, 0xc0, 0x37, 0xb2, 0xcb, 0x19, 0x1b, - 0xc9, 0x94, 0xdf, 0xcc, 0xcf, 0x28, 0x35, 0x99, 0x13, 0x9a, 0xcc, 0xc2, 0x99, 0xee, 0x9a, 0x04, - 0x8f, 0x7f, 0xdb, 0xb7, 0xf7, 0x1e, 0xa7, 0xe4, 0xf1, 0xed, 0x4c, 0x73, 0x9e, 0x3c, 0xbe, 0x9d, - 0x6d, 0xd2, 0x93, 0xc7, 0xb7, 0x5d, 0x0e, 0x62, 0x10, 0xc7, 0x68, 0xb7, 0x60, 0x89, 0xcb, 0xfc, - 0x6d, 0x41, 0x0e, 0x45, 0xb3, 0xb4, 0x47, 0xf0, 0xfd, 0xfd, 0x3e, 0xd0, 0x7b, 0x76, 0x78, 0xe5, - 0xbb, 0x07, 0x0d, 0x2b, 0x2d, 0x75, 0x4f, 0x58, 0x6a, 0x03, 0xea, 0xb9, 0xab, 0x01, 0xc3, 0xc3, - 0x7e, 0xdb, 0x68, 0x69, 0x4f, 0xe2, 0xaf, 0x0b, 0xe0, 0xe5, 0x2c, 0xfd, 0x16, 0x5c, 0xeb, 0xe1, - 0xa1, 0x4f, 0xed, 0x24, 0xcb, 0xef, 0x1d, 0x20, 0xa2, 0xb4, 0x94, 0x29, 0x2c, 0x75, 0x1f, 0x7e, - 0x98, 0xc7, 0x52, 0xf1, 0xf1, 0x52, 0xf7, 0x2a, 0xe2, 0xbf, 0x0a, 0x98, 0xd8, 0x65, 0x5a, 0x00, - 0x97, 0x7a, 0x99, 0x35, 0x84, 0x86, 0xb9, 0xd6, 0x1b, 0x48, 0xfe, 0xf8, 0x8a, 0x34, 0xde, 0x35, - 0xbe, 0xfe, 0xa3, 0xc8, 0x16, 0x31, 0xad, 0x13, 0x86, 0x39, 0x26, 0x2c, 0x7b, 0x74, 0xdb, 0xe5, - 0x95, 0x5e, 0x61, 0xf2, 0x57, 0xcf, 0xbb, 0x34, 0xee, 0xf0, 0x7f, 0xc9, 0x9f, 0xf3, 0xe3, 0xad, - 0x35, 0xbc, 0x9e, 0xff, 0x8a, 0x52, 0xfb, 0xfb, 0xf2, 0x8d, 0xde, 0x81, 0x7a, 0xe8, 0x19, 0x88, - 0xa5, 0x3d, 0x8e, 0xc6, 0x0b, 0x4f, 0xe0, 0x5f, 0xc3, 0x5a, 0x30, 0x96, 0x9e, 0xf2, 0xd4, 0x82, - 0x69, 0x13, 0x84, 0xf2, 0x95, 0x7d, 0xf3, 0x4b, 0xd5, 0x56, 0x84, 0x6a, 0x57, 0xe1, 0xe5, 0xbc, - 0x09, 0x30, 0xee, 0xc5, 0xb5, 0x0f, 0xbe, 0x7c, 0x36, 0xa5, 0x7c, 0xf5, 0x6c, 0x4a, 0xf9, 0xfb, - 0xb3, 0x29, 0xe5, 0x93, 0xe7, 0x53, 0x7d, 0x5f, 0x3d, 0x9f, 0xea, 0xfb, 0xd3, 0xf3, 0xa9, 0xbe, - 0x7b, 0x97, 0xea, 0x84, 0x6d, 0xed, 0x6c, 0x56, 0x4d, 0xb7, 0x21, 0xff, 0x2f, 0xa7, 0xe3, 0xa8, - 0x0b, 0xd1, 0x51, 0xcd, 0xd7, 0xb5, 0x47, 0x89, 0xda, 0xb3, 0xe5, 0x61, 0xba, 0x39, 0x24, 0x7e, - 0x3d, 0xfc, 0xde, 0xb7, 0x01, 0x00, 0x00, 0xff, 0xff, 0x19, 0x33, 0x42, 0x36, 0x37, 0x25, 0x00, - 0x00, + 0x15, 0x17, 0x57, 0x1f, 0x5e, 0xcd, 0x5a, 0x72, 0x32, 0x96, 0xad, 0xf5, 0xca, 0xd1, 0xca, 0x74, + 0xdc, 0x2a, 0x72, 0xbc, 0x2b, 0xa9, 0xc8, 0x97, 0x13, 0x7f, 0x68, 0x65, 0x49, 0x16, 0x1c, 0xdb, + 0x0a, 0xa5, 0x38, 0x80, 0x53, 0x97, 0x1d, 0x91, 0x93, 0xd5, 0x54, 0x5c, 0x92, 0xe6, 0x8c, 0xd6, + 0xde, 0x1a, 0xbe, 0xf4, 0x94, 0x43, 0x0b, 0x24, 0x08, 0x7a, 0x6e, 0xce, 0x3d, 0x14, 0x45, 0x11, + 0xf4, 0x1f, 0xe8, 0x25, 0xb7, 0xa6, 0xe9, 0xa5, 0x48, 0x51, 0xb7, 0xb0, 0x5b, 0xa0, 0x97, 0x1e, + 0x9a, 0x16, 0x3d, 0x17, 0x33, 0x1c, 0x72, 0x97, 0x34, 0x57, 0x4b, 0x6a, 0x75, 0x5b, 0xce, 0xbc, + 0xf7, 0x9b, 0xf7, 0xde, 0xbc, 0xf7, 0xe6, 0xbd, 0x27, 0x81, 0x2a, 0xb1, 0x19, 0xf6, 0x8c, 0x1d, + 0x44, 0x6c, 0x9d, 0x62, 0x63, 0xcf, 0x23, 0xac, 0x55, 0x35, 0x8c, 0x66, 0xd5, 0xf5, 0x9c, 0x26, + 0x31, 0xb1, 0x57, 0x6d, 0x2e, 0x54, 0xef, 0xef, 0x61, 0xaf, 0x55, 0x71, 0x3d, 0x87, 0x39, 0xf0, + 0x6c, 0x02, 0x43, 0xc5, 0x30, 0x9a, 0x95, 0x80, 0xa1, 0xd2, 0x5c, 0x28, 0x9d, 0xae, 0x3b, 0x4e, + 0xdd, 0xc2, 0x55, 0xe4, 0x92, 0x2a, 0xb2, 0x6d, 0x87, 0x21, 0x46, 0x1c, 0x9b, 0xfa, 0x10, 0xa5, + 0x89, 0xba, 0x53, 0x77, 0xc4, 0xcf, 0x2a, 0xff, 0x25, 0x57, 0xcb, 0x92, 0x47, 0x7c, 0x6d, 0xef, + 0x7d, 0x54, 0x65, 0xa4, 0x81, 0x29, 0x43, 0x0d, 0x57, 0x12, 0x2c, 0xa6, 0x11, 0x35, 0x94, 0xc2, + 0xe7, 0x99, 0xef, 0xc6, 0xd3, 0x5c, 0xa8, 0xd2, 0x1d, 0xe4, 0x61, 0x53, 0x37, 0x1c, 0x9b, 0xee, + 0x35, 0x42, 0x8e, 0x73, 0xfb, 0x70, 0x3c, 0x20, 0x1e, 0x96, 0x64, 0xa7, 0x19, 0xb6, 0x4d, 0xec, + 0x35, 0x88, 0xcd, 0xaa, 0x86, 0xd7, 0x72, 0x99, 0x53, 0xdd, 0xc5, 0xad, 0x40, 0xc3, 0x53, 0x86, + 0x43, 0x1b, 0x0e, 0xd5, 0x7d, 0x25, 0xfd, 0x0f, 0xb9, 0xf5, 0xb2, 0xff, 0x55, 0xa5, 0x0c, 0xed, + 0x12, 0xbb, 0x5e, 0x6d, 0x2e, 0x6c, 0x63, 0x86, 0x16, 0x82, 0x6f, 0x49, 0x35, 0x27, 0xa9, 0xb6, + 0x11, 0xc5, 0xbe, 0xf9, 0x43, 0x42, 0x17, 0xd5, 0x89, 0x2d, 0xec, 0xe9, 0xd3, 0xaa, 0x97, 0xc1, + 0xd4, 0x7b, 0x9c, 0x62, 0x59, 0x2a, 0xb2, 0x86, 0x6d, 0x4c, 0x09, 0xd5, 0xf0, 0xfd, 0x3d, 0x4c, + 0x19, 0x2c, 0x83, 0x42, 0xa0, 0xa2, 0x4e, 0xcc, 0xa2, 0x32, 0xa3, 0xcc, 0x8e, 0x6a, 0x20, 0x58, + 0x5a, 0x37, 0xd5, 0x47, 0xe0, 0x74, 0x32, 0x3f, 0x75, 0x1d, 0x9b, 0x62, 0xf8, 0x21, 0x18, 0xab, + 0xfb, 0x4b, 0x3a, 0x65, 0x88, 0x61, 0x01, 0x51, 0x58, 0x9c, 0xaf, 0x74, 0xf3, 0x84, 0xe6, 0x42, + 0x25, 0x86, 0xb5, 0xc9, 0xf9, 0x6a, 0x43, 0x5f, 0x3e, 0x29, 0x0f, 0x68, 0x47, 0xeb, 0x1d, 0x6b, + 0xea, 0xaf, 0x14, 0x50, 0x8a, 0x9c, 0xbe, 0xcc, 0xf1, 0x42, 0xe1, 0xaf, 0x83, 0x61, 0x77, 0x07, + 0x51, 0xff, 0xcc, 0xf1, 0xc5, 0xc5, 0x4a, 0x0a, 0xef, 0x0b, 0x0f, 0xdf, 0xe0, 0x9c, 0x9a, 0x0f, + 0x00, 0x57, 0x01, 0x68, 0x5b, 0xae, 0x98, 0x13, 0x2a, 0x7c, 0xa7, 0x22, 0xaf, 0x86, 0x9b, 0xb9, + 0xe2, 0x7b, 0xb9, 0x34, 0x73, 0x65, 0x03, 0xd5, 0xb1, 0x94, 0x42, 0xeb, 0xe0, 0x54, 0x7f, 0xa9, + 0xc4, 0xcc, 0x1d, 0x08, 0x2c, 0xad, 0x55, 0x03, 0x23, 0x42, 0x3c, 0x5a, 0x54, 0x66, 0x06, 0x67, + 0x0b, 0x8b, 0x73, 0xe9, 0x44, 0xe6, 0xdb, 0x9a, 0xe4, 0x84, 0x6b, 0x09, 0xb2, 0x7e, 0xb7, 0xa7, + 0xac, 0xbe, 0x00, 0x11, 0x61, 0x3f, 0x1b, 0x06, 0xc3, 0x02, 0x1a, 0x9e, 0x02, 0x79, 0x5f, 0x84, + 0xd0, 0x05, 0x8e, 0x88, 0xef, 0x75, 0x13, 0x4e, 0x81, 0x51, 0xc3, 0x22, 0xd8, 0x66, 0x7c, 0x2f, + 0x27, 0xf6, 0xf2, 0xfe, 0xc2, 0xba, 0x09, 0x8f, 0x83, 0x61, 0xe6, 0xb8, 0xfa, 0xad, 0xe2, 0xe0, + 0x8c, 0x32, 0x3b, 0xa6, 0x0d, 0x31, 0xc7, 0xbd, 0x05, 0xe7, 0x00, 0x6c, 0x10, 0x5b, 0x77, 0x9d, + 0x07, 0xdc, 0xa7, 0x6c, 0xdd, 0xa7, 0x18, 0x9a, 0x51, 0x66, 0x07, 0xb5, 0xf1, 0x06, 0xb1, 0x37, + 0xf8, 0xc6, 0xba, 0xbd, 0xc5, 0x69, 0xe7, 0xc1, 0x44, 0x13, 0x59, 0xc4, 0x44, 0xcc, 0xf1, 0xa8, + 0x64, 0x31, 0x90, 0x5b, 0x1c, 0x16, 0x78, 0xb0, 0xbd, 0x27, 0x98, 0x96, 0x91, 0x0b, 0xe7, 0xc0, + 0x8b, 0xe1, 0xaa, 0x4e, 0x31, 0x13, 0xe4, 0x23, 0x82, 0xfc, 0x58, 0xb8, 0xb1, 0x89, 0x19, 0xa7, + 0x3d, 0x0d, 0x46, 0x91, 0x65, 0x39, 0x0f, 0x2c, 0x42, 0x59, 0xf1, 0xc8, 0xcc, 0xe0, 0xec, 0xa8, + 0xd6, 0x5e, 0x80, 0x25, 0x90, 0x37, 0xb1, 0xdd, 0x12, 0x9b, 0x79, 0xb1, 0x19, 0x7e, 0xc3, 0x89, + 0xc0, 0xb3, 0x46, 0x85, 0xc6, 0xd2, 0x4b, 0x3e, 0x00, 0xf9, 0x06, 0x66, 0xc8, 0x44, 0x0c, 0x15, + 0x81, 0xb0, 0xfb, 0x6b, 0x99, 0x5c, 0xee, 0xa6, 0x64, 0x96, 0xbe, 0x1e, 0x82, 0x71, 0x23, 0x73, + 0x93, 0xf1, 0x28, 0xc7, 0xc5, 0xc2, 0x8c, 0x32, 0x3b, 0xa4, 0xe5, 0x1b, 0xc4, 0xde, 0xe4, 0xdf, + 0xb0, 0x02, 0x8e, 0x0b, 0xa1, 0x75, 0x62, 0x23, 0x83, 0x91, 0x26, 0xd6, 0x9b, 0xc8, 0xa2, 0xc5, + 0xa3, 0x33, 0xca, 0x6c, 0x5e, 0x7b, 0x51, 0x6c, 0xad, 0xcb, 0x9d, 0x3b, 0xc8, 0xa2, 0xf1, 0x90, + 0x1e, 0x8b, 0x87, 0x34, 0x7c, 0x08, 0x4e, 0x85, 0x56, 0xc0, 0xa6, 0xee, 0xe1, 0x07, 0xc8, 0x33, + 0x75, 0x13, 0xdb, 0x4e, 0x83, 0x16, 0xc7, 0x85, 0x5e, 0xef, 0xa4, 0xd2, 0x6b, 0xa9, 0x8d, 0xa2, + 0x09, 0x90, 0x6b, 0x02, 0x43, 0x9b, 0x44, 0xc9, 0x1b, 0x50, 0x05, 0x47, 0x5d, 0x8f, 0x38, 0x1c, + 0x4c, 0x98, 0xfd, 0x98, 0x30, 0x7b, 0x64, 0x4d, 0xfd, 0x99, 0x02, 0xce, 0x88, 0x10, 0xba, 0x13, + 0xdc, 0x66, 0x60, 0xbe, 0x25, 0xd3, 0xf4, 0x82, 0xd0, 0xbf, 0x04, 0x5e, 0x08, 0x24, 0xd1, 0x91, + 0x69, 0x7a, 0x98, 0x52, 0xdf, 0x73, 0x6b, 0xf0, 0xdb, 0x27, 0xe5, 0xf1, 0x16, 0x6a, 0x58, 0x17, + 0x55, 0xb9, 0xa1, 0x6a, 0xc7, 0x02, 0xda, 0x25, 0x7f, 0x25, 0x6e, 0xa3, 0x5c, 0xdc, 0x46, 0x17, + 0xf3, 0x1f, 0x7f, 0x5e, 0x1e, 0xf8, 0xe7, 0xe7, 0xe5, 0x01, 0xf5, 0x36, 0x50, 0xf7, 0x13, 0x47, + 0x06, 0xf6, 0x2b, 0xe0, 0x85, 0x10, 0x30, 0x22, 0x8f, 0x76, 0xcc, 0xe8, 0xa0, 0xe7, 0xd2, 0x3c, + 0xaf, 0xe0, 0x46, 0x87, 0x74, 0x1d, 0x0a, 0x26, 0x03, 0x26, 0x2b, 0x18, 0x3b, 0xa4, 0x2f, 0x05, + 0xa3, 0xe2, 0xb4, 0x15, 0x4c, 0x36, 0xf8, 0x73, 0xc6, 0x55, 0xa7, 0xc0, 0x29, 0x01, 0xb8, 0xb5, + 0xe3, 0x39, 0x8c, 0x59, 0x58, 0xe4, 0x72, 0xa9, 0x97, 0xfa, 0x87, 0x20, 0xa5, 0xc7, 0x76, 0xe5, + 0x31, 0x65, 0x50, 0xa0, 0x16, 0xa2, 0x3b, 0x7a, 0x03, 0x33, 0xec, 0x89, 0x13, 0x06, 0x35, 0x20, + 0x96, 0x6e, 0xf2, 0x15, 0xb8, 0x08, 0x4e, 0x74, 0x10, 0xe8, 0xc2, 0xd3, 0x90, 0x6d, 0x60, 0xa1, + 0xe2, 0xa0, 0x76, 0xbc, 0x4d, 0xba, 0x14, 0x6c, 0xc1, 0x1f, 0x80, 0xa2, 0x8d, 0x1f, 0x32, 0xdd, + 0xc3, 0xae, 0x85, 0x6d, 0x42, 0x77, 0x74, 0x03, 0xd9, 0x26, 0x57, 0x16, 0x8b, 0xcc, 0x55, 0x58, + 0x2c, 0x55, 0xfc, 0xfa, 0xa2, 0x12, 0xd4, 0x17, 0x95, 0xad, 0xa0, 0xbe, 0xa8, 0xe5, 0x79, 0xb0, + 0x7e, 0xf2, 0xd7, 0xb2, 0xa2, 0x9d, 0xe4, 0x28, 0x5a, 0x00, 0xb2, 0x1c, 0x60, 0xa8, 0xaf, 0x82, + 0x39, 0xa1, 0x92, 0x86, 0xeb, 0xdc, 0xe7, 0x3d, 0x6c, 0x06, 0x3e, 0x12, 0x09, 0x0b, 0x69, 0x81, + 0x15, 0x70, 0x3e, 0x15, 0xb5, 0xb4, 0xc8, 0x49, 0x30, 0x22, 0x43, 0x53, 0x11, 0xd1, 0x22, 0xbf, + 0xd4, 0x77, 0xc1, 0x2b, 0x02, 0x66, 0xc9, 0xb2, 0x36, 0x10, 0xf1, 0xe8, 0x1d, 0x64, 0x71, 0x1c, + 0x7e, 0x09, 0xb5, 0x56, 0x1b, 0x31, 0xe5, 0x33, 0xff, 0x0b, 0x45, 0xea, 0xd0, 0x03, 0x4e, 0x0a, + 0x75, 0x1f, 0xbc, 0xe8, 0x22, 0xe2, 0xf1, 0x4c, 0xc4, 0x4b, 0x24, 0xe1, 0x11, 0xf2, 0x49, 0x5b, + 0x4d, 0x95, 0x3a, 0xf8, 0x19, 0xfe, 0x11, 0xfc, 0x84, 0xd0, 0xe3, 0xec, 0xb6, 0x2d, 0xc6, 0xdd, + 0x08, 0x89, 0xfa, 0x5f, 0x05, 0x9c, 0xe9, 0xc9, 0x05, 0x57, 0xbb, 0xe6, 0x85, 0xa9, 0x6f, 0x9f, + 0x94, 0x27, 0xfd, 0xb0, 0x89, 0x53, 0x24, 0x24, 0x88, 0xd5, 0x84, 0xf0, 0xcb, 0xc5, 0x71, 0xe2, + 0x14, 0x09, 0x71, 0x78, 0x05, 0x1c, 0x0d, 0xa9, 0x76, 0x71, 0x4b, 0xba, 0xdb, 0xe9, 0x4a, 0xbb, + 0x40, 0xac, 0xf8, 0x05, 0x62, 0x65, 0x63, 0x6f, 0xdb, 0x22, 0xc6, 0x0d, 0xdc, 0xd2, 0xc2, 0xab, + 0xba, 0x81, 0x5b, 0xea, 0x04, 0x80, 0xe2, 0x5e, 0x36, 0x90, 0x87, 0xda, 0x3e, 0xf4, 0x43, 0x70, + 0x3c, 0xb2, 0x2a, 0xaf, 0x65, 0x1d, 0x8c, 0xb8, 0x62, 0x45, 0x56, 0x61, 0xe7, 0x53, 0xde, 0x05, + 0x67, 0x91, 0x8f, 0x92, 0x04, 0x50, 0x6f, 0x4a, 0x7f, 0x88, 0x14, 0x32, 0xb7, 0x5d, 0x86, 0xcd, + 0x75, 0x3b, 0xcc, 0x14, 0xe9, 0xcb, 0xc8, 0xfb, 0xd2, 0xe9, 0x7b, 0xc1, 0x85, 0x75, 0xd2, 0x4b, + 0x9d, 0x75, 0x41, 0xec, 0xbe, 0x70, 0x10, 0x0b, 0x53, 0x1d, 0x05, 0x42, 0xf4, 0x02, 0x31, 0x55, + 0x97, 0xc0, 0x74, 0xe4, 0xc8, 0x03, 0x48, 0xfd, 0xe9, 0x11, 0x30, 0xd3, 0x05, 0x23, 0xfc, 0xd5, + 0xef, 0x53, 0x14, 0xf7, 0x90, 0x5c, 0x46, 0x0f, 0x81, 0x45, 0x30, 0x2c, 0x0a, 0x27, 0xe1, 0x5b, + 0x83, 0xb5, 0x5c, 0x51, 0xd1, 0xfc, 0x05, 0xf8, 0x16, 0x18, 0xf2, 0x78, 0x8e, 0x1b, 0x12, 0xd2, + 0x9c, 0xe3, 0xf7, 0xfb, 0xcd, 0x93, 0xf2, 0x94, 0x5f, 0x2a, 0x52, 0x73, 0xb7, 0x42, 0x9c, 0x6a, + 0x03, 0xb1, 0x9d, 0xca, 0xbb, 0xb8, 0x8e, 0x8c, 0xd6, 0x35, 0x6c, 0x14, 0x15, 0x4d, 0xb0, 0xc0, + 0x73, 0x60, 0x3c, 0x94, 0xca, 0x47, 0x1f, 0x16, 0xf9, 0x75, 0x2c, 0x58, 0x15, 0x05, 0x19, 0xbc, + 0x07, 0x8a, 0x21, 0x99, 0xe1, 0x34, 0x1a, 0x84, 0x52, 0xe2, 0xd8, 0xba, 0x38, 0x75, 0x44, 0x9c, + 0x7a, 0x36, 0xc5, 0xa9, 0xda, 0xc9, 0x00, 0x64, 0x39, 0xc4, 0xd0, 0xb8, 0x14, 0xf7, 0x40, 0x31, + 0x34, 0x6d, 0x1c, 0xfe, 0x48, 0x06, 0xf8, 0x00, 0x24, 0x06, 0x7f, 0x03, 0x14, 0x4c, 0x4c, 0x0d, + 0x8f, 0xb8, 0xa2, 0x94, 0xce, 0x0b, 0xcb, 0x9f, 0x0d, 0x4a, 0xe9, 0xa0, 0xe7, 0x0a, 0xea, 0xe8, + 0x6b, 0x6d, 0x52, 0x19, 0x2b, 0x9d, 0xdc, 0xf0, 0x1e, 0x38, 0x15, 0xca, 0xea, 0xb8, 0xd8, 0x13, + 0x05, 0x6a, 0xe0, 0x0f, 0xa2, 0x8c, 0xac, 0x9d, 0xf9, 0xfa, 0x8b, 0x0b, 0x2f, 0x49, 0xf4, 0xd0, + 0x7f, 0xa4, 0x1f, 0x6c, 0x32, 0x8f, 0xd8, 0x75, 0x6d, 0x32, 0xc0, 0xb8, 0x2d, 0x21, 0x02, 0x37, + 0x39, 0x09, 0x46, 0x7e, 0x84, 0x88, 0x85, 0x4d, 0x51, 0x79, 0xe6, 0x35, 0xf9, 0x05, 0x2f, 0x82, + 0x11, 0xde, 0x77, 0xed, 0x51, 0x51, 0x37, 0x8e, 0x2f, 0xaa, 0xdd, 0xc4, 0xaf, 0x39, 0xb6, 0xb9, + 0x29, 0x28, 0x35, 0xc9, 0x01, 0xb7, 0x40, 0xe8, 0x8d, 0x3a, 0x73, 0x76, 0xb1, 0xed, 0x57, 0x95, + 0xa3, 0xb5, 0xf3, 0xd2, 0xaa, 0x27, 0x9e, 0xb7, 0xea, 0xba, 0xcd, 0xbe, 0xfe, 0xe2, 0x02, 0x90, + 0x87, 0xac, 0xdb, 0x4c, 0x1b, 0x0f, 0x30, 0xb6, 0x04, 0x04, 0x77, 0x9d, 0x10, 0xd5, 0x77, 0x9d, + 0x31, 0xdf, 0x75, 0x82, 0x55, 0xdf, 0x75, 0x5e, 0x07, 0x93, 0x32, 0x7a, 0x31, 0xd5, 0x8d, 0x3d, + 0xcf, 0xe3, 0x3d, 0x06, 0x76, 0x1d, 0x63, 0x47, 0xd4, 0xa0, 0x79, 0xed, 0x44, 0xb8, 0xbd, 0xec, + 0xef, 0xae, 0xf0, 0x4d, 0xf5, 0x63, 0x05, 0x94, 0xbb, 0xc6, 0xb5, 0x4c, 0x1f, 0x18, 0x80, 0x76, + 0x66, 0x90, 0xef, 0xd2, 0x4a, 0xaa, 0x5c, 0xd8, 0x2b, 0xda, 0xb5, 0x0e, 0x60, 0xf5, 0x3e, 0x98, + 0x4f, 0x68, 0xf6, 0x42, 0xda, 0xeb, 0x88, 0x6e, 0x39, 0xf2, 0x0b, 0x1f, 0x4e, 0xe1, 0xaa, 0xde, + 0x01, 0x0b, 0x19, 0x8e, 0x94, 0xe6, 0x38, 0xd3, 0x91, 0x62, 0x88, 0x19, 0x24, 0xcf, 0x42, 0x3b, + 0xd1, 0x89, 0xa2, 0xf4, 0x7c, 0x72, 0x99, 0x1b, 0x8d, 0x99, 0xb4, 0xa9, 0x33, 0x51, 0xcf, 0x5c, + 0x7a, 0x3d, 0xeb, 0xe0, 0xd5, 0x74, 0xe2, 0x48, 0x15, 0xdf, 0x90, 0xa9, 0x4e, 0x49, 0x9f, 0x15, + 0x04, 0x83, 0xaa, 0xca, 0x0c, 0x5f, 0xb3, 0x1c, 0x63, 0x97, 0xbe, 0x6f, 0x33, 0x62, 0xdd, 0xc2, + 0x0f, 0x7d, 0x5f, 0x0b, 0x5e, 0xdb, 0xbb, 0xb2, 0x60, 0x4f, 0xa6, 0x91, 0x12, 0xbc, 0x06, 0x26, + 0xb7, 0xc5, 0xbe, 0xbe, 0xc7, 0x09, 0x74, 0x51, 0x71, 0xfa, 0xfe, 0xac, 0x88, 0x8e, 0x6e, 0x62, + 0x3b, 0x81, 0x5d, 0x5d, 0x92, 0xd5, 0xf7, 0x72, 0x68, 0xba, 0x55, 0xcf, 0x69, 0x2c, 0xcb, 0x0e, + 0x3b, 0x30, 0x77, 0xa4, 0x0b, 0x57, 0xa2, 0x5d, 0xb8, 0xba, 0x0a, 0xce, 0xee, 0x0b, 0xd1, 0x2e, + 0xad, 0xf7, 0x7f, 0xed, 0xde, 0x91, 0x75, 0x7b, 0xc4, 0xb7, 0x52, 0xbf, 0x95, 0xbf, 0x1b, 0x4c, + 0x9a, 0xd5, 0xa4, 0x3e, 0x3d, 0x32, 0x83, 0xc8, 0x45, 0x67, 0x10, 0x67, 0xc1, 0x98, 0xf3, 0xc0, + 0xee, 0x70, 0xa4, 0x41, 0xb1, 0x7f, 0x54, 0x2c, 0x06, 0x09, 0x32, 0x6c, 0xd9, 0x87, 0xba, 0xb5, + 0xec, 0xc3, 0x87, 0xd9, 0xb2, 0x7f, 0x04, 0x0a, 0xc4, 0x26, 0x4c, 0x97, 0xf5, 0xd6, 0x88, 0xc0, + 0x5e, 0xc9, 0x84, 0xbd, 0x6e, 0x13, 0x46, 0x90, 0x45, 0x7e, 0x2c, 0xc6, 0x31, 0xa2, 0x0a, 0xe3, + 0x7d, 0x0b, 0xd5, 0x00, 0x47, 0xf6, 0xab, 0x32, 0xd8, 0x00, 0x13, 0xfe, 0x58, 0x84, 0xee, 0x20, + 0x97, 0xd8, 0xf5, 0xe0, 0xc0, 0x23, 0xe2, 0xc0, 0xb7, 0xd3, 0x15, 0x78, 0x1c, 0x60, 0xd3, 0xe7, + 0xef, 0x38, 0x06, 0xba, 0xf1, 0x75, 0xaa, 0xd6, 0x62, 0xc9, 0x55, 0x8e, 0xe8, 0x78, 0x37, 0x94, + 0xda, 0x13, 0x76, 0x63, 0x45, 0x53, 0x04, 0x43, 0xba, 0xc3, 0x1a, 0x08, 0x26, 0x7d, 0x3a, 0x23, + 0x8d, 0x60, 0x6a, 0x98, 0xae, 0x0d, 0x2b, 0xd4, 0xdb, 0x80, 0x8b, 0xdf, 0x94, 0xc1, 0xb0, 0x38, + 0x0d, 0xfe, 0x43, 0x01, 0x13, 0x49, 0xe7, 0xc2, 0xab, 0xd9, 0x33, 0x7f, 0x74, 0x4a, 0x5a, 0x5a, + 0xea, 0x03, 0xc1, 0x57, 0x58, 0xbd, 0xfe, 0x93, 0x3f, 0xfe, 0xfd, 0xb3, 0x5c, 0x0d, 0x5e, 0xed, + 0x3d, 0x53, 0x0f, 0xad, 0x2b, 0xf5, 0xac, 0x3e, 0xea, 0xb0, 0xf7, 0x63, 0xf8, 0x67, 0x45, 0x16, + 0xff, 0xd1, 0x37, 0x00, 0x5e, 0xc9, 0x2e, 0x64, 0x64, 0x9c, 0x5a, 0xba, 0x7a, 0x70, 0x00, 0xa9, + 0xe4, 0x92, 0x50, 0xf2, 0x6d, 0xf8, 0x56, 0x06, 0x25, 0xfd, 0xa9, 0x66, 0xf5, 0x91, 0x88, 0xd7, + 0xc7, 0xf0, 0xd3, 0x9c, 0x4c, 0x23, 0x89, 0xf3, 0x16, 0xb8, 0x9a, 0x5e, 0xc6, 0xfd, 0xe6, 0x47, + 0xa5, 0xb5, 0xbe, 0x71, 0xa4, 0xca, 0xdb, 0x42, 0xe5, 0xef, 0xc3, 0xbb, 0x29, 0xfe, 0x56, 0x12, + 0xce, 0x2d, 0x23, 0x8d, 0x63, 0xf4, 0x7a, 0xab, 0x8f, 0xe2, 0xcf, 0x66, 0x92, 0x4d, 0x3a, 0xbb, + 0x9d, 0x03, 0xd9, 0x24, 0x61, 0xe4, 0x74, 0x20, 0x9b, 0x24, 0xcd, 0x8a, 0x0e, 0x66, 0x93, 0x88, + 0xda, 0x71, 0x9b, 0xc4, 0x3b, 0xed, 0xc7, 0xf0, 0xf7, 0x8a, 0x6c, 0x8c, 0x23, 0x73, 0x24, 0x78, + 0x39, 0xbd, 0x0e, 0x49, 0xe3, 0xa9, 0xd2, 0x95, 0x03, 0xf3, 0x4b, 0xdd, 0xdf, 0x14, 0xba, 0x2f, + 0xc2, 0xf9, 0xde, 0xba, 0x33, 0x09, 0xe0, 0xff, 0xe1, 0x04, 0xfe, 0x3c, 0x27, 0xdf, 0xf1, 0xfd, + 0x07, 0x43, 0xf0, 0x76, 0x7a, 0x11, 0x53, 0x0d, 0xa4, 0x4a, 0x1b, 0x87, 0x07, 0x28, 0x8d, 0x70, + 0x43, 0x18, 0x61, 0x05, 0x2e, 0xf7, 0x36, 0x82, 0x17, 0x22, 0xb6, 0xa3, 0x22, 0x32, 0x91, 0x86, + 0x3f, 0xcd, 0xc9, 0x12, 0x69, 0xdf, 0xd1, 0x14, 0xbc, 0x95, 0x5e, 0x8b, 0x34, 0x23, 0xb3, 0xd2, + 0xed, 0x43, 0xc3, 0x93, 0x46, 0x59, 0x11, 0x46, 0xb9, 0x02, 0x2f, 0xf5, 0x36, 0x8a, 0xf4, 0x72, + 0xdd, 0xe5, 0xa8, 0xb1, 0xf4, 0xff, 0x1b, 0x05, 0x14, 0x3a, 0x66, 0x3f, 0xf0, 0x8d, 0xf4, 0x72, + 0x46, 0x66, 0x48, 0xa5, 0x37, 0xb3, 0x33, 0x4a, 0x4d, 0xe6, 0x85, 0x26, 0x73, 0x70, 0xb6, 0xb7, + 0x26, 0x7e, 0xb5, 0xd2, 0xf6, 0xed, 0xfd, 0xe7, 0x3f, 0x59, 0x7c, 0x3b, 0xd5, 0x60, 0x2a, 0x8b, + 0x6f, 0xa7, 0x1b, 0x4d, 0x65, 0xf1, 0x6d, 0x87, 0x83, 0xe8, 0xc4, 0xd6, 0xdb, 0x3d, 0x63, 0xec, + 0x32, 0x7f, 0x9b, 0x93, 0x53, 0xdc, 0x34, 0xfd, 0x1c, 0x7c, 0xff, 0xa0, 0x0f, 0xf4, 0xbe, 0x2d, + 0x69, 0xe9, 0xce, 0x61, 0xc3, 0x4a, 0x4b, 0xdd, 0x15, 0x96, 0xda, 0x82, 0x5a, 0xe6, 0x6a, 0x40, + 0x77, 0xb1, 0xd7, 0x36, 0x5a, 0xd2, 0x93, 0xf8, 0xeb, 0x1c, 0x78, 0x39, 0x4d, 0x83, 0x08, 0x37, + 0xfa, 0x78, 0xe8, 0x13, 0x5b, 0xdf, 0xd2, 0x7b, 0x87, 0x88, 0x28, 0x2d, 0x65, 0x08, 0x4b, 0xdd, + 0x83, 0x1f, 0x66, 0xb1, 0x54, 0x74, 0x1e, 0xd6, 0xbb, 0x8a, 0xf8, 0xb7, 0x02, 0x26, 0xbb, 0x8c, + 0x37, 0xe0, 0x72, 0x3f, 0xc3, 0x91, 0xc0, 0x30, 0xd7, 0xfa, 0x03, 0xc9, 0x1e, 0x5f, 0xa1, 0xc6, + 0x5d, 0xe3, 0xeb, 0x5f, 0x8a, 0xec, 0x69, 0x93, 0x5a, 0x77, 0x98, 0x61, 0x24, 0xb4, 0xcf, 0x78, + 0xa0, 0xb4, 0xda, 0x2f, 0x4c, 0xf6, 0xea, 0xb9, 0xcb, 0xa4, 0x01, 0xfe, 0x27, 0xfe, 0xff, 0x07, + 0xd1, 0x59, 0x00, 0x5c, 0xcb, 0x7e, 0x45, 0x89, 0x03, 0x89, 0xd2, 0xf5, 0xfe, 0x81, 0xfa, 0xe8, + 0x19, 0x88, 0x59, 0x7d, 0x14, 0xce, 0x43, 0x1e, 0xc3, 0xbf, 0x04, 0xb5, 0x60, 0x24, 0x3d, 0x65, + 0xa9, 0x05, 0x93, 0x46, 0x1e, 0xa5, 0x2b, 0x07, 0xe6, 0x97, 0xaa, 0xad, 0x0a, 0xd5, 0xae, 0xc2, + 0xcb, 0x59, 0x13, 0x60, 0xcc, 0x8b, 0xff, 0xa7, 0x80, 0x62, 0xb7, 0x8e, 0x1a, 0x5e, 0x3b, 0x70, + 0x6f, 0xda, 0xd1, 0xd4, 0x97, 0x56, 0xfa, 0x44, 0x91, 0x1a, 0xdf, 0x14, 0x1a, 0xaf, 0xc1, 0x95, + 0xec, 0x5d, 0xae, 0x98, 0x03, 0x44, 0x15, 0xaf, 0x7d, 0xf0, 0xe5, 0xd3, 0x69, 0xe5, 0xab, 0xa7, + 0xd3, 0xca, 0xdf, 0x9e, 0x4e, 0x2b, 0x9f, 0x3c, 0x9b, 0x1e, 0xf8, 0xea, 0xd9, 0xf4, 0xc0, 0x9f, + 0x9e, 0x4d, 0x0f, 0xdc, 0xbd, 0x54, 0x27, 0x6c, 0x67, 0x6f, 0xbb, 0x62, 0x38, 0x0d, 0xf9, 0x1f, + 0x54, 0x1d, 0x27, 0x5e, 0x08, 0x4f, 0x6c, 0xbe, 0x5e, 0x7d, 0x18, 0x2b, 0xba, 0x5b, 0x2e, 0xa6, + 0xdb, 0x23, 0x62, 0xc0, 0xf0, 0xbd, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x93, 0x7a, 0xb2, 0x53, + 0xe1, 0x26, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2084,6 +2178,9 @@ type QueryClient interface { // QueryConsumerChain returns the consumer chain // associated with the provided consumer id QueryConsumerChain(ctx context.Context, in *QueryConsumerChainRequest, opts ...grpc.CallOption) (*QueryConsumerChainResponse, error) + // QueryConsumerGenesisTime returns the genesis time + // of the consumer chain associated with the provided consumer id + QueryConsumerGenesisTime(ctx context.Context, in *QueryConsumerGenesisTimeRequest, opts ...grpc.CallOption) (*QueryConsumerGenesisTimeResponse, error) } type queryClient struct { @@ -2229,6 +2326,15 @@ func (c *queryClient) QueryConsumerChain(ctx context.Context, in *QueryConsumerC return out, nil } +func (c *queryClient) QueryConsumerGenesisTime(ctx context.Context, in *QueryConsumerGenesisTimeRequest, opts ...grpc.CallOption) (*QueryConsumerGenesisTimeResponse, error) { + out := new(QueryConsumerGenesisTimeResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Query/QueryConsumerGenesisTime", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // ConsumerGenesis queries the genesis state needed to start a consumer chain @@ -2276,6 +2382,9 @@ type QueryServer interface { // QueryConsumerChain returns the consumer chain // associated with the provided consumer id QueryConsumerChain(context.Context, *QueryConsumerChainRequest) (*QueryConsumerChainResponse, error) + // QueryConsumerGenesisTime returns the genesis time + // of the consumer chain associated with the provided consumer id + QueryConsumerGenesisTime(context.Context, *QueryConsumerGenesisTimeRequest) (*QueryConsumerGenesisTimeResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -2327,6 +2436,9 @@ func (*UnimplementedQueryServer) QueryConsumerIdFromClientId(ctx context.Context func (*UnimplementedQueryServer) QueryConsumerChain(ctx context.Context, req *QueryConsumerChainRequest) (*QueryConsumerChainResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryConsumerChain not implemented") } +func (*UnimplementedQueryServer) QueryConsumerGenesisTime(ctx context.Context, req *QueryConsumerGenesisTimeRequest) (*QueryConsumerGenesisTimeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryConsumerGenesisTime not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -2602,6 +2714,24 @@ func _Query_QueryConsumerChain_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _Query_QueryConsumerGenesisTime_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConsumerGenesisTimeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryConsumerGenesisTime(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryConsumerGenesisTime", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryConsumerGenesisTime(ctx, req.(*QueryConsumerGenesisTimeRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "interchain_security.ccv.provider.v1.Query", HandlerType: (*QueryServer)(nil), @@ -2666,6 +2796,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryConsumerChain", Handler: _Query_QueryConsumerChain_Handler, }, + { + MethodName: "QueryConsumerGenesisTime", + Handler: _Query_QueryConsumerGenesisTime_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "interchain_security/ccv/provider/v1/query.proto", @@ -4009,6 +4143,67 @@ func (m *QueryConsumerChainResponse) MarshalToSizedBuffer(dAtA []byte) (int, err return len(dAtA) - i, nil } +func (m *QueryConsumerGenesisTimeRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConsumerGenesisTimeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerGenesisTimeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConsumerId) > 0 { + i -= len(m.ConsumerId) + copy(dAtA[i:], m.ConsumerId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConsumerId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConsumerGenesisTimeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConsumerGenesisTimeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerGenesisTimeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n14, err14 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.GenesisTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.GenesisTime):]) + if err14 != nil { + return 0, err14 + } + i -= n14 + i = encodeVarintQuery(dAtA, i, uint64(n14)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -4581,6 +4776,30 @@ func (m *QueryConsumerChainResponse) Size() (n int) { return n } +func (m *QueryConsumerGenesisTimeRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConsumerId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryConsumerGenesisTimeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.GenesisTime) + n += 1 + l + sovQuery(uint64(l)) + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -8354,6 +8573,171 @@ func (m *QueryConsumerChainResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryConsumerGenesisTimeRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConsumerGenesisTimeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerGenesisTimeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConsumerId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConsumerGenesisTimeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConsumerGenesisTimeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerGenesisTimeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GenesisTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.GenesisTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ccv/provider/types/query.pb.gw.go b/x/ccv/provider/types/query.pb.gw.go index f332e03b69..923105a51f 100644 --- a/x/ccv/provider/types/query.pb.gw.go +++ b/x/ccv/provider/types/query.pb.gw.go @@ -789,6 +789,60 @@ func local_request_Query_QueryConsumerChain_0(ctx context.Context, marshaler run } +func request_Query_QueryConsumerGenesisTime_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerGenesisTimeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["consumer_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "consumer_id") + } + + protoReq.ConsumerId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "consumer_id", err) + } + + msg, err := client.QueryConsumerGenesisTime(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryConsumerGenesisTime_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerGenesisTimeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["consumer_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "consumer_id") + } + + protoReq.ConsumerId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "consumer_id", err) + } + + msg, err := server.QueryConsumerGenesisTime(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -1140,6 +1194,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_QueryConsumerGenesisTime_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryConsumerGenesisTime_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryConsumerGenesisTime_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1481,6 +1558,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_QueryConsumerGenesisTime_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryConsumerGenesisTime_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryConsumerGenesisTime_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1514,6 +1611,8 @@ var ( pattern_Query_QueryConsumerIdFromClientId_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "consumer_id", "client_id"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryConsumerChain_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "consumer_chain", "consumer_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryConsumerGenesisTime_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "consumer_genesis_time", "consumer_id"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -1546,4 +1645,6 @@ var ( forward_Query_QueryConsumerIdFromClientId_0 = runtime.ForwardResponseMessage forward_Query_QueryConsumerChain_0 = runtime.ForwardResponseMessage + + forward_Query_QueryConsumerGenesisTime_0 = runtime.ForwardResponseMessage ) From af5d7a4b1eb7a18648228d4b3322b1d1697278e8 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 6 Nov 2024 12:05:31 +0100 Subject: [PATCH 10/11] feat!: allow the chain id to be updated if the chain has not yet launched (#2378) * init commit * add CHANGELOG files * took into account comments * removed unecessary check * add a small comment in the CLI of UpdateConsumer that NewChainId can be empty --- .../features/2378-allow-chain-id-updates.md | 2 + .../2378-allow-chain-id-updates.md | 2 + docs/docs/build/modules/02-provider.md | 6 + .../ccv/provider/v1/tx.proto | 5 + x/ccv/provider/client/cli/tx.go | 3 +- x/ccv/provider/keeper/msg_server.go | 21 +- x/ccv/provider/keeper/msg_server_test.go | 63 +++- x/ccv/provider/keeper/permissionless.go | 7 + x/ccv/provider/keeper/permissionless_test.go | 20 ++ x/ccv/provider/types/msg.go | 36 +- x/ccv/provider/types/msg_test.go | 55 +++- x/ccv/provider/types/tx.pb.go | 307 +++++++++++------- 12 files changed, 385 insertions(+), 142 deletions(-) create mode 100644 .changelog/unreleased/features/2378-allow-chain-id-updates.md create mode 100644 .changelog/unreleased/state-breaking/2378-allow-chain-id-updates.md diff --git a/.changelog/unreleased/features/2378-allow-chain-id-updates.md b/.changelog/unreleased/features/2378-allow-chain-id-updates.md new file mode 100644 index 0000000000..cab2fdc62b --- /dev/null +++ b/.changelog/unreleased/features/2378-allow-chain-id-updates.md @@ -0,0 +1,2 @@ +- Allow the chain id of a consumer chain to be updated before the chain + launches. ([\#2378](https://github.com/cosmos/interchain-security/pull/2378)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/2378-allow-chain-id-updates.md b/.changelog/unreleased/state-breaking/2378-allow-chain-id-updates.md new file mode 100644 index 0000000000..cab2fdc62b --- /dev/null +++ b/.changelog/unreleased/state-breaking/2378-allow-chain-id-updates.md @@ -0,0 +1,2 @@ +- Allow the chain id of a consumer chain to be updated before the chain + launches. ([\#2378](https://github.com/cosmos/interchain-security/pull/2378)) \ No newline at end of file diff --git a/docs/docs/build/modules/02-provider.md b/docs/docs/build/modules/02-provider.md index 2b00d273c9..ef7d475193 100644 --- a/docs/docs/build/modules/02-provider.md +++ b/docs/docs/build/modules/02-provider.md @@ -538,6 +538,9 @@ If the `power_shaping_parameters` field is set and `power_shaping_parameters.top If the `new_owner_address` field is set to a value different than the gov module account address, then `top_N` needs to be zero. +We can also update the `chain_id` of a consumer chain by using the optional `new_chain_id` field. Note that the chain id of a consumer chain +can only be updated if the chain has not yet launched. After launch, the chain id of a consumer chain cannot be updated anymore. + ```proto message MsgUpdateConsumer { option (cosmos.msg.v1.signer) = "owner"; @@ -562,6 +565,9 @@ message MsgUpdateConsumer { // allowlisted reward denoms by the consumer chain AllowlistedRewardDenoms allowlisted_reward_denoms = 7; + + // to update the chain id of the chain (can only be updated if the chain has not yet launched) + string new_chain_id = 8; } ``` diff --git a/proto/interchain_security/ccv/provider/v1/tx.proto b/proto/interchain_security/ccv/provider/v1/tx.proto index ea4c8e503e..ea86835759 100644 --- a/proto/interchain_security/ccv/provider/v1/tx.proto +++ b/proto/interchain_security/ccv/provider/v1/tx.proto @@ -394,6 +394,11 @@ message MsgUpdateConsumer { // allowlisted reward denoms of the consumer (if provided they overwrite previously set reward denoms) AllowlistedRewardDenoms allowlisted_reward_denoms = 7; + + // (optional) If the consumer chain has NOT yet launched, the chain id can be updated. After a chain has launched + // the chain id CANNOT be updated. + // This field is optional and can remain empty (i.e., `new_chain_id = ""`) or correspond to the chain id the chain already has. + string new_chain_id = 8; } // MsgUpdateConsumerResponse defines response type for MsgUpdateConsumer messages diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index 52a53f3ebe..1894ad7b36 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -359,6 +359,7 @@ where update_consumer.json has the following structure: "allowlisted_reward_denoms": { "denoms": ["ibc/...", "ibc/..."] } + "new_chain_id": "newConsumer-1", // is optional and can be empty (i.e., "new_chain_id": "") } Note that only 'consumer_id' is mandatory. The others are optional. @@ -397,7 +398,7 @@ If one of the fields is missing, it will be set to its zero value. } msg, err := types.NewMsgUpdateConsumer(owner, consUpdate.ConsumerId, consUpdate.NewOwnerAddress, consUpdate.Metadata, - consUpdate.InitializationParameters, consUpdate.PowerShapingParameters, consUpdate.AllowlistedRewardDenoms) + consUpdate.InitializationParameters, consUpdate.PowerShapingParameters, consUpdate.AllowlistedRewardDenoms, consUpdate.NewChainId) if err != nil { return err } diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index 89970c4b70..d55a8a1201 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -474,6 +474,22 @@ func (k msgServer) UpdateConsumer(goCtx context.Context, msg *types.MsgUpdateCon return &resp, errorsmod.Wrapf(ccvtypes.ErrInvalidConsumerState, "cannot get consumer chain ID: %s", err.Error()) } + // We only validate and use `NewChainId` if it is not empty (because `NewChainId` is an optional argument) + // or `NewChainId` is different from the current chain id of the consumer chain. + if strings.TrimSpace(msg.NewChainId) != "" && msg.NewChainId != chainId { + if err = types.ValidateChainId("NewChainId", msg.NewChainId); err != nil { + return &resp, errorsmod.Wrapf(types.ErrInvalidMsgUpdateConsumer, "invalid new chain id: %s", err.Error()) + } + + if k.IsConsumerPrelaunched(ctx, consumerId) { + chainId = msg.NewChainId + k.SetConsumerChainId(ctx, consumerId, chainId) + } else { + // the chain id cannot be updated if the chain is NOT in a prelaunched (i.e., registered or initialized) phase + return &resp, errorsmod.Wrapf(types.ErrInvalidPhase, "cannot update chain id of a non-prelaunched chain: %s", k.GetConsumerPhase(ctx, consumerId)) + } + } + // add event attributes eventAttributes = append(eventAttributes, []sdk.Attribute{ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), @@ -511,14 +527,13 @@ func (k msgServer) UpdateConsumer(goCtx context.Context, msg *types.MsgUpdateCon previousSpawnTime := previousInitializationParameters.SpawnTime if msg.InitializationParameters != nil { - phase := k.GetConsumerPhase(ctx, consumerId) - - if phase == types.CONSUMER_PHASE_LAUNCHED { + if !k.IsConsumerPrelaunched(ctx, consumerId) { return &resp, errorsmod.Wrap(types.ErrInvalidMsgUpdateConsumer, "cannot update the initialization parameters of an an already launched chain; "+ "do not provide any initialization parameters when updating a launched chain") } + phase := k.GetConsumerPhase(ctx, consumerId) if msg.InitializationParameters.SpawnTime.IsZero() { if phase == types.CONSUMER_PHASE_INITIALIZED { // chain was previously ready to launch at `previousSpawnTime` so we remove the diff --git a/x/ccv/provider/keeper/msg_server_test.go b/x/ccv/provider/keeper/msg_server_test.go index 7100f44079..9e60ab484f 100644 --- a/x/ccv/provider/keeper/msg_server_test.go +++ b/x/ccv/provider/keeper/msg_server_test.go @@ -82,9 +82,10 @@ func TestUpdateConsumer(t *testing.T) { require.Error(t, err, "cannot update consumer chain") // create a chain before updating it + chainId := "chainId-1" createConsumerResponse, err := msgServer.CreateConsumer(ctx, &providertypes.MsgCreateConsumer{ - Submitter: "submitter", ChainId: "chainId-1", + Submitter: "submitter", ChainId: chainId, Metadata: providertypes.ConsumerMetadata{ Name: "name", Description: "description", @@ -106,6 +107,32 @@ func TestUpdateConsumer(t *testing.T) { }) require.Error(t, err, "expected owner address") + // assert that we can change the chain id of a registered chain + expectedChainId := "newChainId-1" + _, err = msgServer.UpdateConsumer(ctx, + &providertypes.MsgUpdateConsumer{ + Owner: "submitter", ConsumerId: consumerId, + Metadata: nil, + InitializationParameters: nil, + PowerShapingParameters: nil, + NewChainId: expectedChainId, + }) + require.NoError(t, err) + chainId, err = providerKeeper.GetConsumerChainId(ctx, consumerId) + require.NoError(t, err) + require.Equal(t, expectedChainId, chainId) + + // assert that we cannot change the chain to that of a reserved chain id + _, err = msgServer.UpdateConsumer(ctx, + &providertypes.MsgUpdateConsumer{ + Owner: "submitter", ConsumerId: consumerId, + Metadata: nil, + InitializationParameters: nil, + PowerShapingParameters: nil, + NewChainId: "stride-1", // reversed chain id + }) + require.ErrorContains(t, err, "cannot use a reserved chain id") + expectedConsumerMetadata := providertypes.ConsumerMetadata{ Name: "name2", Description: "description2", @@ -117,12 +144,14 @@ func TestUpdateConsumer(t *testing.T) { expectedPowerShapingParameters := testkeeper.GetTestPowerShapingParameters() expectedOwnerAddress := "cosmos1dkas8mu4kyhl5jrh4nzvm65qz588hy9qcz08la" + expectedChainId = "updatedChainId-1" _, err = msgServer.UpdateConsumer(ctx, &providertypes.MsgUpdateConsumer{ Owner: "submitter", ConsumerId: consumerId, NewOwnerAddress: expectedOwnerAddress, Metadata: &expectedConsumerMetadata, InitializationParameters: &expectedInitializationParameters, PowerShapingParameters: &expectedPowerShapingParameters, + NewChainId: expectedChainId, }) require.NoError(t, err) @@ -146,6 +175,11 @@ func TestUpdateConsumer(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedPowerShapingParameters, actualPowerShapingParameters) + // assert that the chain id has been updated + actualChainId, err := providerKeeper.GetConsumerChainId(ctx, consumerId) + require.NoError(t, err) + require.Equal(t, expectedChainId, actualChainId) + // assert phase phase := providerKeeper.GetConsumerPhase(ctx, consumerId) require.Equal(t, providertypes.CONSUMER_PHASE_INITIALIZED, phase) @@ -191,6 +225,33 @@ func TestUpdateConsumer(t *testing.T) { }) require.ErrorContains(t, err, "cannot update the initialization parameters of an an already launched chain") + // assert that we CANNOT change the chain id of a launched chain + providerKeeper.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + _, err = msgServer.UpdateConsumer(ctx, + &providertypes.MsgUpdateConsumer{ + Owner: expectedOwnerAddress, ConsumerId: consumerId, + Metadata: nil, + InitializationParameters: nil, + PowerShapingParameters: nil, + NewChainId: "newChainId", + }) + require.ErrorContains(t, err, "cannot update chain id of a non-prelaunched chain") + + // assert that we can use the chain's current chain id as `NewChainId` even if the chain has launched + // as effectively this does not change anything + chainId, err = providerKeeper.GetConsumerChainId(ctx, consumerId) + require.NoError(t, err) + providerKeeper.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + _, err = msgServer.UpdateConsumer(ctx, + &providertypes.MsgUpdateConsumer{ + Owner: expectedOwnerAddress, ConsumerId: consumerId, + Metadata: nil, + InitializationParameters: nil, + PowerShapingParameters: nil, + NewChainId: chainId, + }) + require.NoError(t, err) + // assert that we can update the consumer metadata of a launched chain providerKeeper.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) expectedConsumerMetadata.Name = "name of a launched chain" diff --git a/x/ccv/provider/keeper/permissionless.go b/x/ccv/provider/keeper/permissionless.go index dc96a5a062..7b55635312 100644 --- a/x/ccv/provider/keeper/permissionless.go +++ b/x/ccv/provider/keeper/permissionless.go @@ -177,6 +177,13 @@ func (k Keeper) DeleteConsumerPhase(ctx sdk.Context, consumerId string) { store.Delete(types.ConsumerIdToPhaseKey(consumerId)) } +// IsConsumerPrelaunched checks if a consumer chain is in its prelaunch phase +func (k Keeper) IsConsumerPrelaunched(ctx sdk.Context, consumerId string) bool { + phase := k.GetConsumerPhase(ctx, consumerId) + return phase == types.CONSUMER_PHASE_REGISTERED || + phase == types.CONSUMER_PHASE_INITIALIZED +} + // IsConsumerActive checks if a consumer chain is either registered, initialized, or launched. func (k Keeper) IsConsumerActive(ctx sdk.Context, consumerId string) bool { phase := k.GetConsumerPhase(ctx, consumerId) diff --git a/x/ccv/provider/keeper/permissionless_test.go b/x/ccv/provider/keeper/permissionless_test.go index 3039d7b282..156fb0a48e 100644 --- a/x/ccv/provider/keeper/permissionless_test.go +++ b/x/ccv/provider/keeper/permissionless_test.go @@ -198,3 +198,23 @@ func TestConsumerPhase(t *testing.T) { phase = providerKeeper.GetConsumerPhase(ctx, CONSUMER_ID) require.Equal(t, providertypes.CONSUMER_PHASE_LAUNCHED, phase) } + +func TestIsConsumerPrelaunched(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + providerKeeper.SetConsumerPhase(ctx, CONSUMER_ID, providertypes.CONSUMER_PHASE_REGISTERED) + require.True(t, providerKeeper.IsConsumerPrelaunched(ctx, CONSUMER_ID)) + + providerKeeper.SetConsumerPhase(ctx, CONSUMER_ID, providertypes.CONSUMER_PHASE_INITIALIZED) + require.True(t, providerKeeper.IsConsumerPrelaunched(ctx, CONSUMER_ID)) + + providerKeeper.SetConsumerPhase(ctx, CONSUMER_ID, providertypes.CONSUMER_PHASE_LAUNCHED) + require.False(t, providerKeeper.IsConsumerPrelaunched(ctx, CONSUMER_ID)) + + providerKeeper.SetConsumerPhase(ctx, CONSUMER_ID, providertypes.CONSUMER_PHASE_STOPPED) + require.False(t, providerKeeper.IsConsumerPrelaunched(ctx, CONSUMER_ID)) + + providerKeeper.SetConsumerPhase(ctx, CONSUMER_ID, providertypes.CONSUMER_PHASE_DELETED) + require.False(t, providerKeeper.IsConsumerPrelaunched(ctx, CONSUMER_ID)) +} diff --git a/x/ccv/provider/types/msg.go b/x/ccv/provider/types/msg.go index 91a5282c4f..bfc433126a 100644 --- a/x/ccv/provider/types/msg.go +++ b/x/ccv/provider/types/msg.go @@ -295,20 +295,35 @@ func NewMsgCreateConsumer(submitter, chainId string, metadata ConsumerMetadata, }, nil } -// ValidateBasic implements the sdk.HasValidateBasic interface. -func (msg MsgCreateConsumer) ValidateBasic() error { - if err := ValidateStringField("ChainId", msg.ChainId, cmttypes.MaxChainIDLen); err != nil { - return errorsmod.Wrapf(ErrInvalidMsgCreateConsumer, "ChainId: %s", err.Error()) - } - +// IsReservedChainId returns true if the specific chain id is reserved and cannot be used by other consumer chains +func IsReservedChainId(chainId string) bool { // With permissionless ICS, we can have multiple consumer chains with the exact same chain id. // However, as we already have the Neutron and Stride Top N chains running, as a first step we would like to // prevent permissionless chains from re-using the chain ids of Neutron and Stride. Note that this is just a // preliminary measure that will be removed later on as part of: // TODO (#2242): find a better way of ignoring past misbehaviors - if msg.ChainId == "neutron-1" || msg.ChainId == "stride-1" { - return errorsmod.Wrapf(ErrInvalidMsgCreateConsumer, - "cannot reuse chain ids of existing Neutron and Stride Top N consumer chains") + return chainId == "neutron-1" || chainId == "stride-1" +} + +// ValidateChainId validates that the chain id is valid and is not reserved. +// Can be called for the `MsgUpdateConsumer.NewChainId` field as well, so this method takes the `field` as an argument +// to return more appropriate error messages in case the validation fails. +func ValidateChainId(field string, chainId string) error { + if err := ValidateStringField(field, chainId, cmttypes.MaxChainIDLen); err != nil { + return errorsmod.Wrapf(ErrInvalidMsgCreateConsumer, "%s: %s", field, err.Error()) + } + + if IsReservedChainId(chainId) { + return errorsmod.Wrapf(ErrInvalidMsgCreateConsumer, "cannot use a reserved chain id") + } + + return nil +} + +// ValidateBasic implements the sdk.HasValidateBasic interface. +func (msg MsgCreateConsumer) ValidateBasic() error { + if err := ValidateChainId("ChainId", msg.ChainId); err != nil { + return errorsmod.Wrapf(ErrInvalidMsgCreateConsumer, "ChainId: %s", err.Error()) } if err := ValidateConsumerMetadata(msg.Metadata); err != nil { @@ -343,7 +358,7 @@ func (msg MsgCreateConsumer) ValidateBasic() error { // NewMsgUpdateConsumer creates a new MsgUpdateConsumer instance func NewMsgUpdateConsumer(owner, consumerId, ownerAddress string, metadata *ConsumerMetadata, initializationParameters *ConsumerInitializationParameters, powerShapingParameters *PowerShapingParameters, - allowlistedRewardDenoms *AllowlistedRewardDenoms, + allowlistedRewardDenoms *AllowlistedRewardDenoms, newChainId string, ) (*MsgUpdateConsumer, error) { return &MsgUpdateConsumer{ Owner: owner, @@ -353,6 +368,7 @@ func NewMsgUpdateConsumer(owner, consumerId, ownerAddress string, metadata *Cons InitializationParameters: initializationParameters, PowerShapingParameters: powerShapingParameters, AllowlistedRewardDenoms: allowlistedRewardDenoms, + NewChainId: newChainId, }, nil } diff --git a/x/ccv/provider/types/msg_test.go b/x/ccv/provider/types/msg_test.go index f1eaa3124a..1c826d350d 100644 --- a/x/ccv/provider/types/msg_test.go +++ b/x/ccv/provider/types/msg_test.go @@ -1,6 +1,7 @@ package types_test import ( + "strings" "testing" "time" @@ -490,6 +491,7 @@ func TestMsgUpdateConsumerValidateBasic(t *testing.T) { testCases := []struct { name string powerShapingParameters types.PowerShapingParameters + newChainId string expPass bool }{ { @@ -504,6 +506,7 @@ func TestMsgUpdateConsumerValidateBasic(t *testing.T) { AllowInactiveVals: false, Prioritylist: []string{consAddr1}, }, + "validchainid-0", true, }, { @@ -516,6 +519,7 @@ func TestMsgUpdateConsumerValidateBasic(t *testing.T) { Denylist: nil, Prioritylist: nil, }, + "validchainid-0", false, }, { @@ -530,6 +534,7 @@ func TestMsgUpdateConsumerValidateBasic(t *testing.T) { AllowInactiveVals: false, Prioritylist: nil, }, + "validchainid-0", false, }, { @@ -544,13 +549,14 @@ func TestMsgUpdateConsumerValidateBasic(t *testing.T) { AllowInactiveVals: false, Prioritylist: []string{consAddr1}, }, + "validchainid-0", true, }, } for _, tc := range testCases { // TODO (PERMISSIONLESS) add more tests - msg, _ := types.NewMsgUpdateConsumer("", "0", "cosmos1p3ucd3ptpw902fluyjzhq3ffgq4ntddac9sa3s", nil, nil, &tc.powerShapingParameters, nil) + msg, _ := types.NewMsgUpdateConsumer("", "0", "cosmos1p3ucd3ptpw902fluyjzhq3ffgq4ntddac9sa3s", nil, nil, &tc.powerShapingParameters, nil, tc.newChainId) err := msg.ValidateBasic() if tc.expPass { require.NoError(t, err, "valid case: %s should not return error. got %w", tc.name, err) @@ -714,3 +720,50 @@ func TestValidateInitialHeight(t *testing.T) { } } } + +func TestValidateChainId(t *testing.T) { + testCases := []struct { + name string + chainId string + expPass bool + }{ + { + name: "valid chain id", + chainId: "chain-1", + expPass: true, + }, + { + name: "valid chain id with no revision", + chainId: "chainId", + expPass: true, + }, + { + name: "invalid (too long) chain id", + chainId: strings.Repeat("thisIsAnExtremelyLongChainId", 2), + expPass: false, + }, + { + name: "reserved chain id", + chainId: "stride-1", + expPass: false, + }, + { + name: "reserved chain id", + chainId: "neutron-1", + expPass: false, + }, + { + name: "empty chain id", + chainId: " ", + expPass: false, + }, + } + for _, tc := range testCases { + err := types.ValidateChainId("ChainId", tc.chainId) + if tc.expPass { + require.NoError(t, err, "valid case: '%s' should not return error. got %w", tc.name, err) + } else { + require.Error(t, err, "invalid case: '%s' must return error but got none", tc.name) + } + } +} diff --git a/x/ccv/provider/types/tx.pb.go b/x/ccv/provider/types/tx.pb.go index 05d4d43966..f2a5989b6a 100644 --- a/x/ccv/provider/types/tx.pb.go +++ b/x/ccv/provider/types/tx.pb.go @@ -1475,6 +1475,10 @@ type MsgUpdateConsumer struct { PowerShapingParameters *PowerShapingParameters `protobuf:"bytes,6,opt,name=power_shaping_parameters,json=powerShapingParameters,proto3" json:"power_shaping_parameters,omitempty"` // allowlisted reward denoms of the consumer (if provided they overwrite previously set reward denoms) AllowlistedRewardDenoms *AllowlistedRewardDenoms `protobuf:"bytes,7,opt,name=allowlisted_reward_denoms,json=allowlistedRewardDenoms,proto3" json:"allowlisted_reward_denoms,omitempty"` + // (optional) If the consumer chain has NOT yet launched, the chain id can be updated. After a chain has launched + // the chain id CANNOT be updated. + // This field is optional and can remain empty (i.e., `new_chain_id = ""`) or correspond to the chain id the chain already has. + NewChainId string `protobuf:"bytes,8,opt,name=new_chain_id,json=newChainId,proto3" json:"new_chain_id,omitempty"` } func (m *MsgUpdateConsumer) Reset() { *m = MsgUpdateConsumer{} } @@ -1559,6 +1563,13 @@ func (m *MsgUpdateConsumer) GetAllowlistedRewardDenoms() *AllowlistedRewardDenom return nil } +func (m *MsgUpdateConsumer) GetNewChainId() string { + if m != nil { + return m.NewChainId + } + return "" +} + // MsgUpdateConsumerResponse defines response type for MsgUpdateConsumer messages type MsgUpdateConsumerResponse struct { } @@ -1630,136 +1641,137 @@ func init() { } var fileDescriptor_43221a4391e9fbf4 = []byte{ - // 2054 bytes of a gzipped FileDescriptorProto + // 2071 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x59, 0x4d, 0x6c, 0x1c, 0x49, 0x15, 0x76, 0x8f, 0xc7, 0xce, 0x4c, 0xd9, 0xf1, 0x4f, 0xd9, 0x59, 0xb7, 0x27, 0x59, 0x8f, 0x33, 0x2c, 0xbb, 0x56, 0x58, 0xf7, 0x6c, 0x0c, 0x1b, 0x84, 0x09, 0x48, 0xfe, 0x09, 0xc4, 0x0b, 0x8e, 0xbd, 0xed, 0x90, 0x95, 0x40, 0xa2, 0x55, 0xd3, 0x5d, 0xe9, 0x29, 0x65, 0xba, 0xab, 0xd5, 0x55, - 0x33, 0x8e, 0x39, 0xa1, 0x3d, 0xed, 0x71, 0x91, 0x38, 0x70, 0xdc, 0x03, 0x1c, 0x90, 0x40, 0xca, - 0x61, 0x8f, 0x5c, 0x91, 0x22, 0x71, 0x59, 0xf6, 0x84, 0x10, 0x0a, 0x28, 0x39, 0x2c, 0x17, 0x2e, - 0xdc, 0x38, 0x81, 0xea, 0xa7, 0x7b, 0xa6, 0x67, 0xc6, 0x76, 0x7b, 0xa2, 0x65, 0x0f, 0x7b, 0x19, - 0x75, 0xd7, 0xfb, 0xde, 0xf7, 0x7e, 0xaa, 0xea, 0xbd, 0xaa, 0x1e, 0xf0, 0x26, 0x09, 0x39, 0x8e, - 0xdd, 0x26, 0x22, 0xa1, 0xc3, 0xb0, 0xdb, 0x8e, 0x09, 0x3f, 0xa9, 0xbb, 0x6e, 0xa7, 0x1e, 0xc5, - 0xb4, 0x43, 0x3c, 0x1c, 0xd7, 0x3b, 0x37, 0xeb, 0xfc, 0xb1, 0x15, 0xc5, 0x94, 0x53, 0xf8, 0x95, - 0x21, 0x68, 0xcb, 0x75, 0x3b, 0x56, 0x82, 0xb6, 0x3a, 0x37, 0x2b, 0xf3, 0x28, 0x20, 0x21, 0xad, - 0xcb, 0x5f, 0xa5, 0x57, 0xb9, 0xe6, 0x53, 0xea, 0xb7, 0x70, 0x1d, 0x45, 0xa4, 0x8e, 0xc2, 0x90, - 0x72, 0xc4, 0x09, 0x0d, 0x99, 0x96, 0x56, 0xb5, 0x54, 0xbe, 0x35, 0xda, 0x0f, 0xeb, 0x9c, 0x04, - 0x98, 0x71, 0x14, 0x44, 0x1a, 0xb0, 0xd2, 0x0f, 0xf0, 0xda, 0xb1, 0x64, 0xd0, 0xf2, 0xe5, 0x7e, - 0x39, 0x0a, 0x4f, 0xb4, 0x68, 0xd1, 0xa7, 0x3e, 0x95, 0x8f, 0x75, 0xf1, 0x94, 0x28, 0xb8, 0x94, - 0x05, 0x94, 0x39, 0x4a, 0xa0, 0x5e, 0xb4, 0x68, 0x49, 0xbd, 0xd5, 0x03, 0xe6, 0x8b, 0xd0, 0x03, - 0xe6, 0x27, 0x5e, 0x92, 0x86, 0x5b, 0x77, 0x69, 0x8c, 0xeb, 0x6e, 0x8b, 0xe0, 0x90, 0x0b, 0xa9, - 0x7a, 0xd2, 0x80, 0x8d, 0x3c, 0xa9, 0x4c, 0x13, 0xa5, 0x74, 0xea, 0x82, 0xb4, 0x45, 0xfc, 0x26, - 0x57, 0x54, 0xac, 0xce, 0x71, 0xe8, 0xe1, 0x38, 0x20, 0xca, 0x40, 0xf7, 0x2d, 0xf1, 0xa2, 0x47, - 0xce, 0x4f, 0x22, 0xcc, 0xea, 0x58, 0xf0, 0x85, 0x2e, 0x56, 0x80, 0xda, 0x7f, 0x0c, 0xb0, 0xb8, - 0xcf, 0xfc, 0x2d, 0xc6, 0x88, 0x1f, 0xee, 0xd0, 0x90, 0xb5, 0x03, 0x1c, 0xff, 0x00, 0x9f, 0xc0, - 0x57, 0x41, 0x49, 0xf9, 0x46, 0x3c, 0xd3, 0x58, 0x35, 0xd6, 0xca, 0xdb, 0x05, 0xd3, 0xb0, 0x2f, - 0xc9, 0xb1, 0x3d, 0x0f, 0x7e, 0x13, 0x5c, 0x4e, 0x7c, 0x73, 0x90, 0xe7, 0xc5, 0x66, 0x41, 0x62, - 0xe0, 0xbf, 0x9f, 0x55, 0x67, 0x4e, 0x50, 0xd0, 0xda, 0xac, 0x89, 0x51, 0xcc, 0x58, 0xcd, 0x9e, - 0x4e, 0x80, 0x5b, 0x9e, 0x17, 0xc3, 0xeb, 0x60, 0xda, 0xd5, 0x66, 0x9c, 0x47, 0xf8, 0xc4, 0x1c, - 0x17, 0x7a, 0xf6, 0x94, 0xdb, 0x63, 0xfa, 0x2d, 0x30, 0x29, 0xbc, 0xc1, 0xb1, 0x59, 0x94, 0xa4, - 0xe6, 0xa7, 0x1f, 0xaf, 0x2f, 0xea, 0xac, 0x6f, 0x29, 0xd6, 0x23, 0x1e, 0x93, 0xd0, 0xb7, 0x35, - 0x0e, 0x56, 0x41, 0x4a, 0x20, 0xfc, 0x9d, 0x90, 0x9c, 0x20, 0x19, 0xda, 0xf3, 0x36, 0x17, 0x3e, - 0xf8, 0xa8, 0x3a, 0xf6, 0xcf, 0x8f, 0xaa, 0x63, 0xef, 0x7f, 0xf6, 0xe4, 0x86, 0xd6, 0xaa, 0xad, - 0x80, 0x6b, 0xc3, 0x42, 0xb7, 0x31, 0x8b, 0x68, 0xc8, 0x70, 0xed, 0xb9, 0x01, 0x5e, 0xdd, 0x67, - 0xfe, 0x51, 0xbb, 0x11, 0x10, 0x9e, 0x00, 0xf6, 0x09, 0x6b, 0xe0, 0x26, 0xea, 0x10, 0xda, 0x8e, - 0xe1, 0x2d, 0x50, 0x66, 0x52, 0xca, 0x71, 0xac, 0xb3, 0x74, 0xba, 0xb3, 0x5d, 0x28, 0x3c, 0x04, - 0xd3, 0x41, 0x0f, 0x8f, 0x4c, 0xde, 0xd4, 0xc6, 0x9b, 0x16, 0x69, 0xb8, 0x56, 0xef, 0xf4, 0x5a, - 0x3d, 0x13, 0xda, 0xb9, 0x69, 0xf5, 0xda, 0xb6, 0x33, 0x0c, 0xfd, 0x19, 0x18, 0x1f, 0xc8, 0xc0, - 0x2b, 0xbd, 0x19, 0xe8, 0xba, 0x52, 0x7b, 0x03, 0x7c, 0xf5, 0xcc, 0x18, 0xd3, 0x6c, 0xfc, 0xb9, - 0x30, 0x24, 0x1b, 0xbb, 0xb4, 0xdd, 0x68, 0xe1, 0x07, 0x94, 0x93, 0xd0, 0x1f, 0x39, 0x1b, 0x0e, - 0x58, 0xf2, 0xda, 0x51, 0x8b, 0xb8, 0x88, 0x63, 0xa7, 0x43, 0x39, 0x76, 0x92, 0x45, 0xaa, 0x13, - 0xf3, 0x46, 0x6f, 0x1e, 0xe4, 0x32, 0xb6, 0x76, 0x13, 0x85, 0x07, 0x94, 0xe3, 0x3b, 0x1a, 0x6e, - 0x5f, 0xf1, 0x86, 0x0d, 0xc3, 0x9f, 0x82, 0x25, 0x12, 0x3e, 0x8c, 0x91, 0x2b, 0x8a, 0x80, 0xd3, - 0x68, 0x51, 0xf7, 0x91, 0xd3, 0xc4, 0xc8, 0xc3, 0xb1, 0x4c, 0xd4, 0xd4, 0xc6, 0xeb, 0xe7, 0x65, - 0xfe, 0xae, 0x44, 0xdb, 0x57, 0xba, 0x34, 0xdb, 0x82, 0x45, 0x0d, 0xf7, 0x27, 0xbf, 0xf8, 0x52, - 0xc9, 0xef, 0x4d, 0x69, 0x9a, 0xfc, 0x5f, 0x1b, 0x60, 0x76, 0x9f, 0xf9, 0x3f, 0x8a, 0x3c, 0xc4, - 0xf1, 0x21, 0x8a, 0x51, 0xc0, 0x44, 0xba, 0x51, 0x9b, 0x37, 0xa9, 0x28, 0x1c, 0xe7, 0xa7, 0x3b, - 0x85, 0xc2, 0x3d, 0x30, 0x19, 0x49, 0x06, 0x9d, 0xdd, 0xaf, 0x59, 0x39, 0xca, 0xb4, 0xa5, 0x8c, - 0x6e, 0x17, 0x9f, 0x3e, 0xab, 0x8e, 0xd9, 0x9a, 0x60, 0x73, 0x46, 0xc6, 0x93, 0x52, 0xd7, 0x96, - 0xc1, 0x52, 0x9f, 0x97, 0x69, 0x04, 0x7f, 0x2b, 0x81, 0x85, 0x7d, 0xe6, 0x27, 0x51, 0x6e, 0x79, - 0x1e, 0x11, 0x69, 0x84, 0xcb, 0xfd, 0x75, 0xa6, 0x5b, 0x63, 0xbe, 0x0f, 0x66, 0x48, 0x48, 0x38, - 0x41, 0x2d, 0xa7, 0x89, 0xc5, 0xdc, 0x68, 0x87, 0x2b, 0x72, 0xb6, 0x44, 0x6d, 0xb5, 0x74, 0x45, - 0x95, 0x33, 0x24, 0x10, 0xda, 0xbf, 0xcb, 0x5a, 0x4f, 0x0d, 0x8a, 0x9a, 0xe3, 0xe3, 0x10, 0x33, - 0xc2, 0x9c, 0x26, 0x62, 0x4d, 0x39, 0xe9, 0xd3, 0xf6, 0x94, 0x1e, 0xbb, 0x8b, 0x58, 0x53, 0x4c, - 0x61, 0x83, 0x84, 0x28, 0x3e, 0x51, 0x88, 0xa2, 0x44, 0x00, 0x35, 0x24, 0x01, 0x3b, 0x00, 0xb0, - 0x08, 0x1d, 0x87, 0x8e, 0xe8, 0x36, 0xb2, 0xc2, 0x08, 0x47, 0x54, 0x27, 0xb1, 0x92, 0x4e, 0x62, - 0xdd, 0x4f, 0x5a, 0xd1, 0x76, 0x49, 0x38, 0xf2, 0xe1, 0xdf, 0xab, 0x86, 0x5d, 0x96, 0x7a, 0x42, - 0x02, 0xef, 0x81, 0xb9, 0x76, 0xd8, 0xa0, 0xa1, 0x47, 0x42, 0xdf, 0x89, 0x70, 0x4c, 0xa8, 0x67, - 0x4e, 0x4a, 0xaa, 0xe5, 0x01, 0xaa, 0x5d, 0xdd, 0xb4, 0x14, 0xd3, 0xaf, 0x04, 0xd3, 0x6c, 0xaa, - 0x7c, 0x28, 0x75, 0xe1, 0xbb, 0x00, 0xba, 0x6e, 0x47, 0xba, 0x44, 0xdb, 0x3c, 0x61, 0xbc, 0x94, - 0x9f, 0x71, 0xce, 0x75, 0x3b, 0xf7, 0x95, 0xb6, 0xa6, 0xfc, 0x09, 0x58, 0xe2, 0x31, 0x0a, 0xd9, - 0x43, 0x1c, 0xf7, 0xf3, 0x96, 0xf2, 0xf3, 0x5e, 0x49, 0x38, 0xb2, 0xe4, 0x77, 0xc1, 0x6a, 0xba, - 0x51, 0x62, 0xec, 0x11, 0xc6, 0x63, 0xd2, 0x68, 0xcb, 0x5d, 0x99, 0xec, 0x2b, 0xb3, 0x2c, 0x17, - 0xc1, 0x4a, 0x82, 0xb3, 0x33, 0xb0, 0xef, 0x69, 0x14, 0x3c, 0x00, 0xaf, 0xc9, 0x7d, 0xcc, 0x84, - 0x73, 0x4e, 0x86, 0x49, 0x9a, 0x0e, 0x08, 0x63, 0x82, 0x0d, 0xac, 0x1a, 0x6b, 0xe3, 0xf6, 0x75, - 0x85, 0x3d, 0xc4, 0xf1, 0x6e, 0x0f, 0xf2, 0x7e, 0x0f, 0x10, 0xae, 0x03, 0xd8, 0x24, 0x8c, 0xd3, - 0x98, 0xb8, 0xa8, 0xe5, 0xe0, 0x90, 0xc7, 0x04, 0x33, 0x73, 0x4a, 0xaa, 0xcf, 0x77, 0x25, 0x77, - 0x94, 0x00, 0xbe, 0x03, 0xae, 0x9f, 0x6a, 0xd4, 0x71, 0x9b, 0x28, 0x0c, 0x71, 0xcb, 0x9c, 0x96, - 0xa1, 0x54, 0xbd, 0x53, 0x6c, 0xee, 0x28, 0x18, 0x5c, 0x00, 0x13, 0x9c, 0x46, 0xce, 0x3d, 0xf3, - 0xf2, 0xaa, 0xb1, 0x76, 0xd9, 0x2e, 0x72, 0x1a, 0xdd, 0x83, 0x6f, 0x81, 0xc5, 0x0e, 0x6a, 0x11, - 0x0f, 0x71, 0x1a, 0x33, 0x27, 0xa2, 0xc7, 0x38, 0x76, 0x5c, 0x14, 0x99, 0x33, 0x12, 0x03, 0xbb, - 0xb2, 0x43, 0x21, 0xda, 0x41, 0x11, 0xbc, 0x01, 0xe6, 0xd3, 0x51, 0x87, 0x61, 0x2e, 0xe1, 0xb3, - 0x12, 0x3e, 0x9b, 0x0a, 0x8e, 0x30, 0x17, 0xd8, 0x6b, 0xa0, 0x8c, 0x5a, 0x2d, 0x7a, 0xdc, 0x22, - 0x8c, 0x9b, 0x73, 0xab, 0xe3, 0x6b, 0x65, 0xbb, 0x3b, 0x00, 0x2b, 0xa0, 0xe4, 0xe1, 0xf0, 0x44, - 0x0a, 0xe7, 0xa5, 0x30, 0x7d, 0xcf, 0x56, 0x1d, 0x98, 0xbf, 0xea, 0x5c, 0x05, 0xe5, 0x40, 0xd4, - 0x17, 0x8e, 0x1e, 0x61, 0x73, 0x61, 0xd5, 0x58, 0x2b, 0xda, 0xa5, 0x80, 0x84, 0x47, 0xe2, 0x1d, - 0x5a, 0x60, 0x41, 0x5a, 0x77, 0x48, 0x28, 0xe6, 0xb7, 0x83, 0x9d, 0x0e, 0x6a, 0x31, 0x73, 0x71, - 0xd5, 0x58, 0x2b, 0xd9, 0xf3, 0x52, 0xb4, 0xa7, 0x25, 0x0f, 0x50, 0x8b, 0x6d, 0xce, 0x65, 0xeb, - 0x8e, 0x69, 0xd4, 0xfe, 0x60, 0x00, 0xd8, 0x53, 0x5e, 0x6c, 0x1c, 0xd0, 0x0e, 0x6a, 0x9d, 0x55, - 0x5d, 0xb6, 0x40, 0x99, 0x89, 0xb4, 0xcb, 0xfd, 0x5c, 0xb8, 0xc0, 0x7e, 0x2e, 0x09, 0x35, 0xb9, - 0x9d, 0x33, 0xb9, 0x18, 0xcf, 0x9d, 0x8b, 0x21, 0xee, 0x47, 0x60, 0x7e, 0x9f, 0xf9, 0xd2, 0x6b, - 0x9c, 0xc4, 0xd0, 0xdf, 0x56, 0x8c, 0xfe, 0xb6, 0x02, 0x2d, 0x30, 0x41, 0x8f, 0xc5, 0x39, 0xa9, - 0x70, 0x8e, 0x6d, 0x05, 0xdb, 0x04, 0xc2, 0xae, 0x7a, 0xae, 0x5d, 0x05, 0xcb, 0x03, 0x16, 0xd3, - 0x62, 0xfd, 0x7b, 0x03, 0x5c, 0x11, 0xd9, 0x6c, 0xa2, 0xd0, 0xc7, 0x36, 0x3e, 0x46, 0xb1, 0xb7, - 0x8b, 0x43, 0x1a, 0x30, 0x58, 0x03, 0x97, 0x3d, 0xf9, 0xe4, 0x70, 0x2a, 0x0e, 0x7e, 0xa6, 0x21, - 0xd7, 0xc7, 0x94, 0x1a, 0xbc, 0x4f, 0xb7, 0x3c, 0x0f, 0xae, 0x81, 0xb9, 0x2e, 0x26, 0x96, 0x16, - 0xcc, 0x82, 0x84, 0xcd, 0x24, 0x30, 0x65, 0x77, 0xe4, 0x04, 0xf6, 0xf7, 0x9d, 0xaa, 0x3c, 0x9a, - 0x0c, 0xba, 0x9b, 0x06, 0xf4, 0x2f, 0x03, 0x94, 0xf6, 0x99, 0x7f, 0x10, 0xf1, 0xbd, 0xf0, 0xcb, - 0x70, 0xb4, 0x85, 0x60, 0x2e, 0x09, 0x37, 0xcd, 0xc1, 0x9f, 0x0c, 0x50, 0x56, 0x83, 0x07, 0x6d, - 0xfe, 0xb9, 0x25, 0xa1, 0x1b, 0xe1, 0xf8, 0x68, 0x11, 0x16, 0xf3, 0x45, 0xb8, 0x20, 0x77, 0x8c, - 0x0a, 0x26, 0x0d, 0xf1, 0x37, 0x05, 0x79, 0xa4, 0x17, 0x45, 0x4e, 0xab, 0xef, 0xd0, 0x40, 0x57, - 0x5b, 0x1b, 0x71, 0x3c, 0x18, 0x96, 0x91, 0x33, 0xac, 0xde, 0x74, 0x15, 0x06, 0xd3, 0x75, 0x07, - 0x14, 0x63, 0xc4, 0xb1, 0x8e, 0xf9, 0xa6, 0xa8, 0x15, 0x7f, 0x7d, 0x56, 0xbd, 0xaa, 0xe2, 0x66, - 0xde, 0x23, 0x8b, 0xd0, 0x7a, 0x80, 0x78, 0xd3, 0xfa, 0x21, 0xf6, 0x91, 0x7b, 0xb2, 0x8b, 0xdd, - 0x4f, 0x3f, 0x5e, 0x07, 0x3a, 0x2d, 0xbb, 0xd8, 0xb5, 0xa5, 0xfa, 0xff, 0x6d, 0x79, 0xbc, 0x0e, - 0x5e, 0x3b, 0x2b, 0x4d, 0x69, 0x3e, 0x9f, 0x8c, 0xcb, 0x03, 0x5d, 0x7a, 0x2f, 0xa0, 0x1e, 0x79, - 0x28, 0x8e, 0xd7, 0xa2, 0x61, 0x2e, 0x82, 0x09, 0x4e, 0x78, 0x0b, 0xeb, 0xba, 0xa4, 0x5e, 0xe0, - 0x2a, 0x98, 0xf2, 0x30, 0x73, 0x63, 0x12, 0xc9, 0x66, 0x5e, 0x50, 0x5b, 0xa0, 0x67, 0x28, 0x53, - 0x92, 0xc7, 0xb3, 0x25, 0x39, 0x6d, 0x84, 0xc5, 0x1c, 0x8d, 0x70, 0xe2, 0x62, 0x8d, 0x70, 0x32, - 0x47, 0x23, 0xbc, 0x74, 0x56, 0x23, 0x2c, 0x9d, 0xd5, 0x08, 0xcb, 0x23, 0x36, 0x42, 0x90, 0xaf, - 0x11, 0x4e, 0xe5, 0x6f, 0x84, 0xd7, 0x41, 0xf5, 0x94, 0x19, 0x4b, 0x67, 0xf5, 0x8f, 0x45, 0xb9, - 0x77, 0x76, 0x62, 0x8c, 0x78, 0xb7, 0xdb, 0x8c, 0x7a, 0x7b, 0x5b, 0xee, 0xdf, 0x19, 0xdd, 0xf9, - 0x7c, 0x0f, 0x94, 0x02, 0xcc, 0x91, 0x87, 0x38, 0xd2, 0x17, 0xad, 0xb7, 0x73, 0xdd, 0x35, 0x52, - 0xef, 0xb5, 0xb2, 0x3e, 0xd5, 0xa7, 0x64, 0xf0, 0x7d, 0x03, 0x2c, 0xeb, 0x23, 0x3e, 0xf9, 0x99, - 0x0c, 0xce, 0x91, 0x37, 0x12, 0xcc, 0x71, 0xcc, 0xe4, 0xea, 0x99, 0xda, 0xb8, 0x73, 0x21, 0x53, - 0x7b, 0x19, 0xb6, 0xc3, 0x94, 0xcc, 0x36, 0xc9, 0x29, 0x12, 0xd8, 0x06, 0xa6, 0x5a, 0x8d, 0xac, - 0x89, 0x22, 0x79, 0xa0, 0xef, 0xba, 0xa0, 0xee, 0x07, 0xdf, 0xce, 0x77, 0xb3, 0x12, 0x24, 0x47, - 0x8a, 0xa3, 0xc7, 0xf0, 0x2b, 0xd1, 0xd0, 0x71, 0xf8, 0x18, 0x2c, 0xa7, 0x0b, 0x14, 0x7b, 0x4e, - 0x2c, 0xdb, 0x9d, 0xa3, 0x1a, 0xab, 0xbe, 0x4c, 0xdc, 0xce, 0x65, 0x77, 0xab, 0xcb, 0x92, 0xe9, - 0x99, 0x4b, 0x68, 0xb8, 0x40, 0x77, 0xdd, 0xee, 0xed, 0xf5, 0xb6, 0x3c, 0x42, 0x64, 0x97, 0x51, - 0xb2, 0xc8, 0xce, 0x3d, 0xbc, 0xd4, 0xfe, 0xab, 0x56, 0xa1, 0xba, 0x2c, 0xa6, 0xab, 0x30, 0x3d, - 0xd2, 0x18, 0xb9, 0x8e, 0x34, 0xfd, 0x66, 0x0a, 0x03, 0x67, 0xa4, 0x5d, 0x30, 0x1f, 0xe2, 0x63, - 0x47, 0xa2, 0x1d, 0x5d, 0xdc, 0xcf, 0x6d, 0x4d, 0xb3, 0x21, 0x3e, 0x3e, 0x10, 0x1a, 0x7a, 0x18, - 0xbe, 0xdb, 0xb3, 0x92, 0x8b, 0x2f, 0xb1, 0x92, 0x73, 0xaf, 0xe1, 0x89, 0x2f, 0x7e, 0x0d, 0x4f, - 0x7e, 0x41, 0x6b, 0xf8, 0xd2, 0xe7, 0xb9, 0x86, 0x07, 0x8f, 0xc0, 0xd9, 0x05, 0x98, 0xac, 0xdf, - 0x8d, 0xa7, 0xd3, 0x60, 0x7c, 0x9f, 0xf9, 0xf0, 0x17, 0x06, 0x98, 0x1f, 0xfc, 0x3a, 0xfa, 0xad, - 0x5c, 0xde, 0x0d, 0xfb, 0xba, 0x58, 0xd9, 0x1a, 0x59, 0x35, 0xdd, 0x5b, 0xbf, 0x33, 0x40, 0xe5, - 0x8c, 0xaf, 0x92, 0xdb, 0x79, 0x2d, 0x9c, 0xce, 0x51, 0x79, 0xe7, 0xe5, 0x39, 0xce, 0x70, 0x37, - 0xf3, 0xd9, 0x70, 0x44, 0x77, 0x7b, 0x39, 0x46, 0x75, 0x77, 0xd8, 0xb7, 0x36, 0xf8, 0x81, 0x01, - 0x66, 0xfa, 0x7b, 0x63, 0x5e, 0xfa, 0xac, 0x5e, 0xe5, 0xbb, 0xa3, 0xe9, 0x65, 0x5c, 0xe9, 0x2b, - 0x90, 0xb9, 0x5d, 0xc9, 0xea, 0xe5, 0x77, 0x65, 0xf8, 0x7e, 0x90, 0xae, 0xf4, 0xdd, 0x4f, 0x73, - 0xbb, 0x92, 0xd5, 0xcb, 0xef, 0xca, 0xf0, 0xdb, 0xa9, 0xa8, 0x9c, 0xd3, 0x99, 0x2f, 0xa1, 0xdf, - 0xb8, 0x58, 0x6c, 0x4a, 0xab, 0x72, 0x7b, 0x14, 0xad, 0xd4, 0x89, 0x00, 0x4c, 0xa8, 0xdb, 0xe4, - 0x7a, 0x5e, 0x1a, 0x09, 0xaf, 0xbc, 0x7d, 0x21, 0x78, 0x6a, 0x2e, 0x02, 0x93, 0xfa, 0xe2, 0x66, - 0x5d, 0x80, 0xe0, 0xa0, 0xcd, 0x2b, 0xb7, 0x2e, 0x86, 0x4f, 0x2d, 0xfe, 0xd6, 0x00, 0xcb, 0xa7, - 0x5f, 0xa4, 0x72, 0x57, 0xb1, 0x53, 0x29, 0x2a, 0x7b, 0x2f, 0x4d, 0x91, 0xfa, 0xfa, 0x4b, 0x03, - 0xc0, 0x21, 0x1f, 0x2b, 0x36, 0x73, 0x6f, 0xbf, 0x01, 0xdd, 0xca, 0xf6, 0xe8, 0xba, 0x89, 0x5b, - 0x95, 0x89, 0x9f, 0x7f, 0xf6, 0xe4, 0x86, 0xb1, 0xfd, 0xde, 0xd3, 0xe7, 0x2b, 0xc6, 0x27, 0xcf, - 0x57, 0x8c, 0x7f, 0x3c, 0x5f, 0x31, 0x3e, 0x7c, 0xb1, 0x32, 0xf6, 0xc9, 0x8b, 0x95, 0xb1, 0xbf, - 0xbc, 0x58, 0x19, 0xfb, 0xf1, 0x77, 0x7c, 0xc2, 0x9b, 0xed, 0x86, 0xe5, 0xd2, 0x40, 0xff, 0xad, - 0x58, 0xef, 0x5a, 0x5d, 0x4f, 0xff, 0x15, 0xec, 0xdc, 0xaa, 0x3f, 0xce, 0xfe, 0x35, 0x28, 0xff, - 0x04, 0x69, 0x4c, 0xca, 0xef, 0x54, 0x5f, 0xff, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xea, 0xeb, - 0x11, 0x01, 0x96, 0x1d, 0x00, 0x00, + 0x33, 0x8e, 0x39, 0xa1, 0x3d, 0xed, 0x71, 0x91, 0x38, 0x70, 0x5c, 0x24, 0x38, 0x20, 0x81, 0x94, + 0xc3, 0x1e, 0xb9, 0x22, 0x45, 0xe2, 0xb2, 0xec, 0x09, 0x21, 0x14, 0x50, 0x72, 0x58, 0x2e, 0x5c, + 0xb8, 0x71, 0x43, 0xf5, 0xd3, 0x3d, 0xd3, 0x33, 0x63, 0xbb, 0x3d, 0xd1, 0xb2, 0x87, 0xbd, 0x58, + 0xd3, 0xf5, 0xbe, 0xf7, 0xbd, 0x9f, 0x7e, 0xf5, 0x5e, 0x55, 0x1b, 0xbc, 0x49, 0x42, 0x8e, 0x63, + 0xb7, 0x89, 0x48, 0xe8, 0x30, 0xec, 0xb6, 0x63, 0xc2, 0x4f, 0xea, 0xae, 0xdb, 0xa9, 0x47, 0x31, + 0xed, 0x10, 0x0f, 0xc7, 0xf5, 0xce, 0xcd, 0x3a, 0x7f, 0x6c, 0x45, 0x31, 0xe5, 0x14, 0x7e, 0x65, + 0x08, 0xda, 0x72, 0xdd, 0x8e, 0x95, 0xa0, 0xad, 0xce, 0xcd, 0xca, 0x3c, 0x0a, 0x48, 0x48, 0xeb, + 0xf2, 0xaf, 0xd2, 0xab, 0x5c, 0xf3, 0x29, 0xf5, 0x5b, 0xb8, 0x8e, 0x22, 0x52, 0x47, 0x61, 0x48, + 0x39, 0xe2, 0x84, 0x86, 0x4c, 0x4b, 0xab, 0x5a, 0x2a, 0x9f, 0x1a, 0xed, 0x87, 0x75, 0x4e, 0x02, + 0xcc, 0x38, 0x0a, 0x22, 0x0d, 0x58, 0xe9, 0x07, 0x78, 0xed, 0x58, 0x32, 0x68, 0xf9, 0x72, 0xbf, + 0x1c, 0x85, 0x27, 0x5a, 0xb4, 0xe8, 0x53, 0x9f, 0xca, 0x9f, 0x75, 0xf1, 0x2b, 0x51, 0x70, 0x29, + 0x0b, 0x28, 0x73, 0x94, 0x40, 0x3d, 0x68, 0xd1, 0x92, 0x7a, 0xaa, 0x07, 0xcc, 0x17, 0xa1, 0x07, + 0xcc, 0x4f, 0xbc, 0x24, 0x0d, 0xb7, 0xee, 0xd2, 0x18, 0xd7, 0xdd, 0x16, 0xc1, 0x21, 0x17, 0x52, + 0xf5, 0x4b, 0x03, 0x36, 0xf2, 0xa4, 0x32, 0x4d, 0x94, 0xd2, 0xa9, 0x0b, 0xd2, 0x16, 0xf1, 0x9b, + 0x5c, 0x51, 0xb1, 0x3a, 0xc7, 0xa1, 0x87, 0xe3, 0x80, 0x28, 0x03, 0xdd, 0xa7, 0xc4, 0x8b, 0x1e, + 0x39, 0x3f, 0x89, 0x30, 0xab, 0x63, 0xc1, 0x17, 0xba, 0x58, 0x01, 0x6a, 0xff, 0x35, 0xc0, 0xe2, + 0x3e, 0xf3, 0xb7, 0x18, 0x23, 0x7e, 0xb8, 0x43, 0x43, 0xd6, 0x0e, 0x70, 0xfc, 0x03, 0x7c, 0x02, + 0x5f, 0x05, 0x25, 0xe5, 0x1b, 0xf1, 0x4c, 0x63, 0xd5, 0x58, 0x2b, 0x6f, 0x17, 0x4c, 0xc3, 0xbe, + 0x24, 0xd7, 0xf6, 0x3c, 0xf8, 0x4d, 0x70, 0x39, 0xf1, 0xcd, 0x41, 0x9e, 0x17, 0x9b, 0x05, 0x89, + 0x81, 0xff, 0x79, 0x56, 0x9d, 0x39, 0x41, 0x41, 0x6b, 0xb3, 0x26, 0x56, 0x31, 0x63, 0x35, 0x7b, + 0x3a, 0x01, 0x6e, 0x79, 0x5e, 0x0c, 0xaf, 0x83, 0x69, 0x57, 0x9b, 0x71, 0x1e, 0xe1, 0x13, 0x73, + 0x5c, 0xe8, 0xd9, 0x53, 0x6e, 0x8f, 0xe9, 0xb7, 0xc0, 0xa4, 0xf0, 0x06, 0xc7, 0x66, 0x51, 0x92, + 0x9a, 0x9f, 0x7e, 0xbc, 0xbe, 0xa8, 0xb3, 0xbe, 0xa5, 0x58, 0x8f, 0x78, 0x4c, 0x42, 0xdf, 0xd6, + 0x38, 0x58, 0x05, 0x29, 0x81, 0xf0, 0x77, 0x42, 0x72, 0x82, 0x64, 0x69, 0xcf, 0xdb, 0x5c, 0xf8, + 0xe0, 0xa3, 0xea, 0xd8, 0xbf, 0x3e, 0xaa, 0x8e, 0xbd, 0xff, 0xd9, 0x93, 0x1b, 0x5a, 0xab, 0xb6, + 0x02, 0xae, 0x0d, 0x0b, 0xdd, 0xc6, 0x2c, 0xa2, 0x21, 0xc3, 0xb5, 0xe7, 0x06, 0x78, 0x75, 0x9f, + 0xf9, 0x47, 0xed, 0x46, 0x40, 0x78, 0x02, 0xd8, 0x27, 0xac, 0x81, 0x9b, 0xa8, 0x43, 0x68, 0x3b, + 0x86, 0xb7, 0x40, 0x99, 0x49, 0x29, 0xc7, 0xb1, 0xce, 0xd2, 0xe9, 0xce, 0x76, 0xa1, 0xf0, 0x10, + 0x4c, 0x07, 0x3d, 0x3c, 0x32, 0x79, 0x53, 0x1b, 0x6f, 0x5a, 0xa4, 0xe1, 0x5a, 0xbd, 0xaf, 0xd7, + 0xea, 0x79, 0xa1, 0x9d, 0x9b, 0x56, 0xaf, 0x6d, 0x3b, 0xc3, 0xd0, 0x9f, 0x81, 0xf1, 0x81, 0x0c, + 0xbc, 0xd2, 0x9b, 0x81, 0xae, 0x2b, 0xb5, 0x37, 0xc0, 0x57, 0xcf, 0x8c, 0x31, 0xcd, 0xc6, 0x5f, + 0x0a, 0x43, 0xb2, 0xb1, 0x4b, 0xdb, 0x8d, 0x16, 0x7e, 0x40, 0x39, 0x09, 0xfd, 0x91, 0xb3, 0xe1, + 0x80, 0x25, 0xaf, 0x1d, 0xb5, 0x88, 0x8b, 0x38, 0x76, 0x3a, 0x94, 0x63, 0x27, 0x29, 0x52, 0x9d, + 0x98, 0x37, 0x7a, 0xf3, 0x20, 0xcb, 0xd8, 0xda, 0x4d, 0x14, 0x1e, 0x50, 0x8e, 0xef, 0x68, 0xb8, + 0x7d, 0xc5, 0x1b, 0xb6, 0x0c, 0x7f, 0x0a, 0x96, 0x48, 0xf8, 0x30, 0x46, 0xae, 0x68, 0x02, 0x4e, + 0xa3, 0x45, 0xdd, 0x47, 0x4e, 0x13, 0x23, 0x0f, 0xc7, 0x32, 0x51, 0x53, 0x1b, 0xaf, 0x9f, 0x97, + 0xf9, 0xbb, 0x12, 0x6d, 0x5f, 0xe9, 0xd2, 0x6c, 0x0b, 0x16, 0xb5, 0xdc, 0x9f, 0xfc, 0xe2, 0x4b, + 0x25, 0xbf, 0x37, 0xa5, 0x69, 0xf2, 0x7f, 0x63, 0x80, 0xd9, 0x7d, 0xe6, 0xff, 0x28, 0xf2, 0x10, + 0xc7, 0x87, 0x28, 0x46, 0x01, 0x13, 0xe9, 0x46, 0x6d, 0xde, 0xa4, 0xa2, 0x71, 0x9c, 0x9f, 0xee, + 0x14, 0x0a, 0xf7, 0xc0, 0x64, 0x24, 0x19, 0x74, 0x76, 0xbf, 0x66, 0xe5, 0x68, 0xd3, 0x96, 0x32, + 0xba, 0x5d, 0x7c, 0xfa, 0xac, 0x3a, 0x66, 0x6b, 0x82, 0xcd, 0x19, 0x19, 0x4f, 0x4a, 0x5d, 0x5b, + 0x06, 0x4b, 0x7d, 0x5e, 0xa6, 0x11, 0xfc, 0xbd, 0x04, 0x16, 0xf6, 0x99, 0x9f, 0x44, 0xb9, 0xe5, + 0x79, 0x44, 0xa4, 0x11, 0x2e, 0xf7, 0xf7, 0x99, 0x6e, 0x8f, 0xf9, 0x3e, 0x98, 0x21, 0x21, 0xe1, + 0x04, 0xb5, 0x9c, 0x26, 0x16, 0xef, 0x46, 0x3b, 0x5c, 0x91, 0x6f, 0x4b, 0xf4, 0x56, 0x4b, 0x77, + 0x54, 0xf9, 0x86, 0x04, 0x42, 0xfb, 0x77, 0x59, 0xeb, 0xa9, 0x45, 0xd1, 0x73, 0x7c, 0x1c, 0x62, + 0x46, 0x98, 0xd3, 0x44, 0xac, 0x29, 0x5f, 0xfa, 0xb4, 0x3d, 0xa5, 0xd7, 0xee, 0x22, 0xd6, 0x14, + 0xaf, 0xb0, 0x41, 0x42, 0x14, 0x9f, 0x28, 0x44, 0x51, 0x22, 0x80, 0x5a, 0x92, 0x80, 0x1d, 0x00, + 0x58, 0x84, 0x8e, 0x43, 0x47, 0x4c, 0x1b, 0xd9, 0x61, 0x84, 0x23, 0x6a, 0x92, 0x58, 0xc9, 0x24, + 0xb1, 0xee, 0x27, 0xa3, 0x68, 0xbb, 0x24, 0x1c, 0xf9, 0xf0, 0x1f, 0x55, 0xc3, 0x2e, 0x4b, 0x3d, + 0x21, 0x81, 0xf7, 0xc0, 0x5c, 0x3b, 0x6c, 0xd0, 0xd0, 0x23, 0xa1, 0xef, 0x44, 0x38, 0x26, 0xd4, + 0x33, 0x27, 0x25, 0xd5, 0xf2, 0x00, 0xd5, 0xae, 0x1e, 0x5a, 0x8a, 0xe9, 0x57, 0x82, 0x69, 0x36, + 0x55, 0x3e, 0x94, 0xba, 0xf0, 0x5d, 0x00, 0x5d, 0xb7, 0x23, 0x5d, 0xa2, 0x6d, 0x9e, 0x30, 0x5e, + 0xca, 0xcf, 0x38, 0xe7, 0xba, 0x9d, 0xfb, 0x4a, 0x5b, 0x53, 0xfe, 0x04, 0x2c, 0xf1, 0x18, 0x85, + 0xec, 0x21, 0x8e, 0xfb, 0x79, 0x4b, 0xf9, 0x79, 0xaf, 0x24, 0x1c, 0x59, 0xf2, 0xbb, 0x60, 0x35, + 0xdd, 0x28, 0x31, 0xf6, 0x08, 0xe3, 0x31, 0x69, 0xb4, 0xe5, 0xae, 0x4c, 0xf6, 0x95, 0x59, 0x96, + 0x45, 0xb0, 0x92, 0xe0, 0xec, 0x0c, 0xec, 0x7b, 0x1a, 0x05, 0x0f, 0xc0, 0x6b, 0x72, 0x1f, 0x33, + 0xe1, 0x9c, 0x93, 0x61, 0x92, 0xa6, 0x03, 0xc2, 0x98, 0x60, 0x03, 0xab, 0xc6, 0xda, 0xb8, 0x7d, + 0x5d, 0x61, 0x0f, 0x71, 0xbc, 0xdb, 0x83, 0xbc, 0xdf, 0x03, 0x84, 0xeb, 0x00, 0x36, 0x09, 0xe3, + 0x34, 0x26, 0x2e, 0x6a, 0x39, 0x38, 0xe4, 0x31, 0xc1, 0xcc, 0x9c, 0x92, 0xea, 0xf3, 0x5d, 0xc9, + 0x1d, 0x25, 0x80, 0xef, 0x80, 0xeb, 0xa7, 0x1a, 0x75, 0xdc, 0x26, 0x0a, 0x43, 0xdc, 0x32, 0xa7, + 0x65, 0x28, 0x55, 0xef, 0x14, 0x9b, 0x3b, 0x0a, 0x06, 0x17, 0xc0, 0x04, 0xa7, 0x91, 0x73, 0xcf, + 0xbc, 0xbc, 0x6a, 0xac, 0x5d, 0xb6, 0x8b, 0x9c, 0x46, 0xf7, 0xe0, 0x5b, 0x60, 0xb1, 0x83, 0x5a, + 0xc4, 0x43, 0x9c, 0xc6, 0xcc, 0x89, 0xe8, 0x31, 0x8e, 0x1d, 0x17, 0x45, 0xe6, 0x8c, 0xc4, 0xc0, + 0xae, 0xec, 0x50, 0x88, 0x76, 0x50, 0x04, 0x6f, 0x80, 0xf9, 0x74, 0xd5, 0x61, 0x98, 0x4b, 0xf8, + 0xac, 0x84, 0xcf, 0xa6, 0x82, 0x23, 0xcc, 0x05, 0xf6, 0x1a, 0x28, 0xa3, 0x56, 0x8b, 0x1e, 0xb7, + 0x08, 0xe3, 0xe6, 0xdc, 0xea, 0xf8, 0x5a, 0xd9, 0xee, 0x2e, 0xc0, 0x0a, 0x28, 0x79, 0x38, 0x3c, + 0x91, 0xc2, 0x79, 0x29, 0x4c, 0x9f, 0xb3, 0x5d, 0x07, 0xe6, 0xef, 0x3a, 0x57, 0x41, 0x39, 0x10, + 0xfd, 0x85, 0xa3, 0x47, 0xd8, 0x5c, 0x58, 0x35, 0xd6, 0x8a, 0x76, 0x29, 0x20, 0xe1, 0x91, 0x78, + 0x86, 0x16, 0x58, 0x90, 0xd6, 0x1d, 0x12, 0x8a, 0xf7, 0xdb, 0xc1, 0x4e, 0x07, 0xb5, 0x98, 0xb9, + 0xb8, 0x6a, 0xac, 0x95, 0xec, 0x79, 0x29, 0xda, 0xd3, 0x92, 0x07, 0xa8, 0xc5, 0x36, 0xe7, 0xb2, + 0x7d, 0xc7, 0x34, 0x6a, 0x7f, 0x34, 0x00, 0xec, 0x69, 0x2f, 0x36, 0x0e, 0x68, 0x07, 0xb5, 0xce, + 0xea, 0x2e, 0x5b, 0xa0, 0xcc, 0x44, 0xda, 0xe5, 0x7e, 0x2e, 0x5c, 0x60, 0x3f, 0x97, 0x84, 0x9a, + 0xdc, 0xce, 0x99, 0x5c, 0x8c, 0xe7, 0xce, 0xc5, 0x10, 0xf7, 0x23, 0x30, 0xbf, 0xcf, 0x7c, 0xe9, + 0x35, 0x4e, 0x62, 0xe8, 0x1f, 0x2b, 0x46, 0xff, 0x58, 0x81, 0x16, 0x98, 0xa0, 0xc7, 0xe2, 0x9c, + 0x54, 0x38, 0xc7, 0xb6, 0x82, 0x6d, 0x02, 0x61, 0x57, 0xfd, 0xae, 0x5d, 0x05, 0xcb, 0x03, 0x16, + 0xd3, 0x66, 0xfd, 0x07, 0x03, 0x5c, 0x11, 0xd9, 0x6c, 0xa2, 0xd0, 0xc7, 0x36, 0x3e, 0x46, 0xb1, + 0xb7, 0x8b, 0x43, 0x1a, 0x30, 0x58, 0x03, 0x97, 0x3d, 0xf9, 0xcb, 0xe1, 0x54, 0x1c, 0xfc, 0x4c, + 0x43, 0xd6, 0xc7, 0x94, 0x5a, 0xbc, 0x4f, 0xb7, 0x3c, 0x0f, 0xae, 0x81, 0xb9, 0x2e, 0x26, 0x96, + 0x16, 0xcc, 0x82, 0x84, 0xcd, 0x24, 0x30, 0x65, 0x77, 0xe4, 0x04, 0xf6, 0xcf, 0x9d, 0xaa, 0x3c, + 0x9a, 0x0c, 0xba, 0x9b, 0x06, 0xf4, 0x6f, 0x03, 0x94, 0xf6, 0x99, 0x7f, 0x10, 0xf1, 0xbd, 0xf0, + 0xcb, 0x70, 0xb4, 0x85, 0x60, 0x2e, 0x09, 0x37, 0xcd, 0xc1, 0x9f, 0x0d, 0x50, 0x56, 0x8b, 0x07, + 0x6d, 0xfe, 0xb9, 0x25, 0xa1, 0x1b, 0xe1, 0xf8, 0x68, 0x11, 0x16, 0xf3, 0x45, 0xb8, 0x20, 0x77, + 0x8c, 0x0a, 0x26, 0x0d, 0xf1, 0xb7, 0x05, 0x79, 0xa4, 0x17, 0x4d, 0x4e, 0xab, 0xef, 0xd0, 0x40, + 0x77, 0x5b, 0x1b, 0x71, 0x3c, 0x18, 0x96, 0x91, 0x33, 0xac, 0xde, 0x74, 0x15, 0x06, 0xd3, 0x75, + 0x07, 0x14, 0x63, 0xc4, 0xb1, 0x8e, 0xf9, 0xa6, 0xe8, 0x15, 0x7f, 0x7b, 0x56, 0xbd, 0xaa, 0xe2, + 0x66, 0xde, 0x23, 0x8b, 0xd0, 0x7a, 0x80, 0x78, 0xd3, 0xfa, 0x21, 0xf6, 0x91, 0x7b, 0xb2, 0x8b, + 0xdd, 0x4f, 0x3f, 0x5e, 0x07, 0x3a, 0x2d, 0xbb, 0xd8, 0xb5, 0xa5, 0xfa, 0xff, 0xad, 0x3c, 0x5e, + 0x07, 0xaf, 0x9d, 0x95, 0xa6, 0x34, 0x9f, 0x4f, 0xc6, 0xe5, 0x81, 0x2e, 0xbd, 0x17, 0x50, 0x8f, + 0x3c, 0x14, 0xc7, 0x6b, 0x31, 0x30, 0x17, 0xc1, 0x04, 0x27, 0xbc, 0x85, 0x75, 0x5f, 0x52, 0x0f, + 0x70, 0x15, 0x4c, 0x79, 0x98, 0xb9, 0x31, 0x89, 0xe4, 0x30, 0x2f, 0xa8, 0x2d, 0xd0, 0xb3, 0x94, + 0x69, 0xc9, 0xe3, 0xd9, 0x96, 0x9c, 0x0e, 0xc2, 0x62, 0x8e, 0x41, 0x38, 0x71, 0xb1, 0x41, 0x38, + 0x99, 0x63, 0x10, 0x5e, 0x3a, 0x6b, 0x10, 0x96, 0xce, 0x1a, 0x84, 0xe5, 0x11, 0x07, 0x21, 0xc8, + 0x37, 0x08, 0xa7, 0xf2, 0x0f, 0xc2, 0xeb, 0xa0, 0x7a, 0xca, 0x1b, 0x4b, 0xdf, 0xea, 0x9f, 0x8a, + 0x72, 0xef, 0xec, 0xc4, 0x18, 0xf1, 0xee, 0xb4, 0x19, 0xf5, 0xf6, 0xb6, 0xdc, 0xbf, 0x33, 0xba, + 0xef, 0xf3, 0x3d, 0x50, 0x0a, 0x30, 0x47, 0x1e, 0xe2, 0x48, 0x5f, 0xb4, 0xde, 0xce, 0x75, 0xd7, + 0x48, 0xbd, 0xd7, 0xca, 0xfa, 0x54, 0x9f, 0x92, 0xc1, 0xf7, 0x0d, 0xb0, 0xac, 0x8f, 0xf8, 0xe4, + 0x67, 0x32, 0x38, 0x47, 0xde, 0x48, 0x30, 0xc7, 0x31, 0x93, 0xd5, 0x33, 0xb5, 0x71, 0xe7, 0x42, + 0xa6, 0xf6, 0x32, 0x6c, 0x87, 0x29, 0x99, 0x6d, 0x92, 0x53, 0x24, 0xb0, 0x0d, 0x4c, 0x55, 0x8d, + 0xac, 0x89, 0x22, 0x79, 0xa0, 0xef, 0xba, 0xa0, 0xee, 0x07, 0xdf, 0xce, 0x77, 0xb3, 0x12, 0x24, + 0x47, 0x8a, 0xa3, 0xc7, 0xf0, 0x2b, 0xd1, 0xd0, 0x75, 0xf8, 0x18, 0x2c, 0xa7, 0x05, 0x8a, 0x3d, + 0x27, 0x96, 0xe3, 0xce, 0x51, 0x83, 0x55, 0x5f, 0x26, 0x6e, 0xe7, 0xb2, 0xbb, 0xd5, 0x65, 0xc9, + 0xcc, 0xcc, 0x25, 0x34, 0x5c, 0xa0, 0xa7, 0x6e, 0xf7, 0xf6, 0x7a, 0x5b, 0x1e, 0x21, 0xb2, 0x65, + 0x94, 0x14, 0xd9, 0xb9, 0x87, 0x97, 0xda, 0xaf, 0x27, 0x64, 0x15, 0xaa, 0xcb, 0x62, 0x5a, 0x85, + 0xe9, 0x91, 0xc6, 0xc8, 0x75, 0xa4, 0xe9, 0x37, 0x53, 0x18, 0x38, 0x23, 0xed, 0x82, 0xf9, 0x10, + 0x1f, 0x3b, 0x12, 0xed, 0xe8, 0xe6, 0x7e, 0xee, 0x68, 0x9a, 0x0d, 0xf1, 0xf1, 0x81, 0xd0, 0xd0, + 0xcb, 0xf0, 0xdd, 0x9e, 0x4a, 0x2e, 0xbe, 0x44, 0x25, 0xe7, 0xae, 0xe1, 0x89, 0x2f, 0xbe, 0x86, + 0x27, 0xbf, 0xa0, 0x1a, 0xbe, 0xf4, 0x39, 0xd6, 0x30, 0x5c, 0x05, 0xd3, 0xa2, 0x1c, 0xd2, 0x8e, + 0x55, 0x52, 0x05, 0x13, 0xe2, 0xe3, 0x1d, 0xd5, 0xb4, 0x86, 0x1c, 0x92, 0xb3, 0x25, 0x9a, 0x54, + 0xf8, 0xc6, 0xd3, 0x69, 0x30, 0xbe, 0xcf, 0x7c, 0xf8, 0x0b, 0x03, 0xcc, 0x0f, 0x7e, 0x3f, 0xfd, + 0x56, 0x2e, 0xff, 0x87, 0x7d, 0x7f, 0xac, 0x6c, 0x8d, 0xac, 0x9a, 0xee, 0xbe, 0xdf, 0x1b, 0xa0, + 0x72, 0xc6, 0x77, 0xcb, 0xed, 0xbc, 0x16, 0x4e, 0xe7, 0xa8, 0xbc, 0xf3, 0xf2, 0x1c, 0x67, 0xb8, + 0x9b, 0xf9, 0xb0, 0x38, 0xa2, 0xbb, 0xbd, 0x1c, 0xa3, 0xba, 0x3b, 0xec, 0x6b, 0x1c, 0xfc, 0xc0, + 0x00, 0x33, 0xfd, 0xd3, 0x33, 0x2f, 0x7d, 0x56, 0xaf, 0xf2, 0xdd, 0xd1, 0xf4, 0x32, 0xae, 0xf4, + 0xb5, 0xd0, 0xdc, 0xae, 0x64, 0xf5, 0xf2, 0xbb, 0x32, 0x7c, 0x3f, 0x48, 0x57, 0xfa, 0x6e, 0xb0, + 0xb9, 0x5d, 0xc9, 0xea, 0xe5, 0x77, 0x65, 0xf8, 0xfd, 0x55, 0xf4, 0xd6, 0xe9, 0xcc, 0xb7, 0xd2, + 0x6f, 0x5c, 0x2c, 0x36, 0xa5, 0x55, 0xb9, 0x3d, 0x8a, 0x56, 0xea, 0x44, 0x00, 0x26, 0xd4, 0x7d, + 0x73, 0x3d, 0x2f, 0x8d, 0x84, 0x57, 0xde, 0xbe, 0x10, 0x3c, 0x35, 0x17, 0x81, 0x49, 0x7d, 0xb5, + 0xb3, 0x2e, 0x40, 0x70, 0xd0, 0xe6, 0x95, 0x5b, 0x17, 0xc3, 0xa7, 0x16, 0x7f, 0x67, 0x80, 0xe5, + 0xd3, 0xaf, 0x5a, 0xb9, 0xbb, 0xd8, 0xa9, 0x14, 0x95, 0xbd, 0x97, 0xa6, 0x48, 0x7d, 0xfd, 0xa5, + 0x01, 0xe0, 0x90, 0xcf, 0x19, 0x9b, 0xb9, 0xb7, 0xdf, 0x80, 0x6e, 0x65, 0x7b, 0x74, 0xdd, 0xc4, + 0xad, 0xca, 0xc4, 0xcf, 0x3f, 0x7b, 0x72, 0xc3, 0xd8, 0x7e, 0xef, 0xe9, 0xf3, 0x15, 0xe3, 0x93, + 0xe7, 0x2b, 0xc6, 0x3f, 0x9f, 0xaf, 0x18, 0x1f, 0xbe, 0x58, 0x19, 0xfb, 0xe4, 0xc5, 0xca, 0xd8, + 0x5f, 0x5f, 0xac, 0x8c, 0xfd, 0xf8, 0x3b, 0x3e, 0xe1, 0xcd, 0x76, 0xc3, 0x72, 0x69, 0xa0, 0xff, + 0xf1, 0x58, 0xef, 0x5a, 0x5d, 0x4f, 0xff, 0x6f, 0xd8, 0xb9, 0x55, 0x7f, 0x9c, 0xfd, 0xe7, 0xa1, + 0xfc, 0x37, 0x49, 0x63, 0x52, 0x7e, 0xc9, 0xfa, 0xfa, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x84, + 0xa1, 0x79, 0x6d, 0xb8, 0x1d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -3347,6 +3359,13 @@ func (m *MsgUpdateConsumer) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.NewChainId) > 0 { + i -= len(m.NewChainId) + copy(dAtA[i:], m.NewChainId) + i = encodeVarintTx(dAtA, i, uint64(len(m.NewChainId))) + i-- + dAtA[i] = 0x42 + } if m.AllowlistedRewardDenoms != nil { { size, err := m.AllowlistedRewardDenoms.MarshalToSizedBuffer(dAtA[:i]) @@ -3981,6 +4000,10 @@ func (m *MsgUpdateConsumer) Size() (n int) { l = m.AllowlistedRewardDenoms.Size() n += 1 + l + sovTx(uint64(l)) } + l = len(m.NewChainId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -7720,6 +7743,38 @@ func (m *MsgUpdateConsumer) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From b9ecf44215e45458b0b2b3fd27306de94bc4bce4 Mon Sep 17 00:00:00 2001 From: bernd-m <43466467+bermuell@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:33:10 +0100 Subject: [PATCH 11/11] fix: update consumer genesis transform (#2373) * add genesis transform v5 * addressed comments * addressed more comments --- .../2373-consumer-backward-compatibility.md | 2 + app/consumer/genesis.go | 218 +---- app/consumer/genesis_test.go | 751 +++++------------- 3 files changed, 212 insertions(+), 759 deletions(-) create mode 100644 .changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md diff --git a/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md b/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md new file mode 100644 index 0000000000..11ee6732cc --- /dev/null +++ b/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md @@ -0,0 +1,2 @@ +- `[x/consumer]` Updated `genesis transform` CLI to transform `consumer-genesis` content exported by v6.2 providers for consumer chains at version v5. Removed transformation for older consumer versions. + ([\#2373](https://github.com/cosmos/interchain-security/pull/2373)) \ No newline at end of file diff --git a/app/consumer/genesis.go b/app/consumer/genesis.go index be56824856..e16c2f4b0e 100644 --- a/app/consumer/genesis.go +++ b/app/consumer/genesis.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "path/filepath" - "regexp" "strings" "github.com/spf13/cobra" @@ -16,7 +15,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - consumerTypes "github.com/cosmos/interchain-security/v6/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v6/x/ccv/types" ) @@ -33,152 +31,50 @@ type GenesisState map[string]json.RawMessage type IcsVersion string const ( - v2_x IcsVersion = "v2.x" - v3_0_x IcsVersion = "v3.0.x" - v3_1_x IcsVersion = "v3.1.x" - v3_2_x IcsVersion = "v3.2.x" - v3_3_x IcsVersion = "v3.3.x" v4_x_x IcsVersion = "v4.x" + v5_x_x IcsVersion = "v5.x" ) var TransformationVersions map[string]IcsVersion = map[string]IcsVersion{ - "v2.x": v2_x, - "v3.0.x": v3_0_x, - "v3.1.x": v3_1_x, - "v3.2.x": v3_2_x, - "v3.3.x": v3_3_x, - "v4.x": v4_x_x, + "v4.x": v4_x_x, + "v5.x": v5_x_x, } -// Transformation of consumer genesis content as it is exported from a provider version v1,2,3 -// to a format readable by current consumer implementation. -func transformToNew(jsonRaw []byte, ctx client.Context) (json.RawMessage, error) { - // v1,2,3 uses deprecated fields of GenesisState type - oldConsumerGenesis := consumerTypes.GenesisState{} - err := ctx.Codec.UnmarshalJSON(jsonRaw, &oldConsumerGenesis) - if err != nil { - return nil, fmt.Errorf("reading consumer genesis data failed: %s", err) - } - - initialValSet := oldConsumerGenesis.InitialValSet - // transformation from >= v3.3.x - if len(initialValSet) == 0 { - initialValSet = oldConsumerGenesis.Provider.InitialValSet - } - - clientState := oldConsumerGenesis.ProviderClientState - if clientState == nil { - clientState = oldConsumerGenesis.Provider.ClientState - } - - consensusState := oldConsumerGenesis.ProviderConsensusState - if consensusState == nil { - consensusState = oldConsumerGenesis.Provider.ConsensusState - } - - // Use DefaultRetryDelayPeriod if not set - if oldConsumerGenesis.Params.RetryDelayPeriod == 0 { - oldConsumerGenesis.Params.RetryDelayPeriod = types.DefaultRetryDelayPeriod - } - - // `SoftOptOutThreshold` is deprecated in the current consumer implementation, so set to zero - oldConsumerGenesis.Params.SoftOptOutThreshold = "0" - - // Versions before v3.3.x of provider genesis data fills up deprecated fields - // ProviderClientState, ConsensusState and InitialValSet in type GenesisState - newGenesis := types.ConsumerGenesisState{ - Params: oldConsumerGenesis.Params, - Provider: types.ProviderInfo{ - ClientState: clientState, - ConsensusState: consensusState, - InitialValSet: initialValSet, - }, - NewChain: oldConsumerGenesis.NewChain, - } - - newJson, err := ctx.Codec.MarshalJSON(&newGenesis) - if err != nil { - return nil, fmt.Errorf("failed marshalling data to new type: %s", err) - } - return newJson, nil -} - -// Transformation of consumer genesis content as it is exported by current provider version -// to a format supported by consumer version v3.3.x -func transformToV33(jsonRaw []byte, ctx client.Context) ([]byte, error) { - // v1,2,3 uses deprecated fields of GenesisState type - srcConGen := consumerTypes.GenesisState{} - err := ctx.Codec.UnmarshalJSON(jsonRaw, &srcConGen) - if err != nil { - return nil, fmt.Errorf("reading consumer genesis data failed: %s", err) - } - - // Remove retry_delay_period from 'params' - params, err := ctx.Codec.MarshalJSON(&srcConGen.Params) - if err != nil { - return nil, err - } - tmp := map[string]json.RawMessage{} - if err := json.Unmarshal(params, &tmp); err != nil { +// Remove a parameter from a JSON object +func removeParameterFromParams(params json.RawMessage, param string) (json.RawMessage, error) { + paramsMap := map[string]json.RawMessage{} + if err := json.Unmarshal(params, ¶msMap); err != nil { return nil, fmt.Errorf("unmarshalling 'params' failed: %v", err) } - _, exists := tmp["retry_delay_period"] + _, exists := paramsMap[param] if exists { - delete(tmp, "retry_delay_period") + delete(paramsMap, param) } - params, err = json.Marshal(tmp) - if err != nil { - return nil, err - } - - // Marshal GenesisState and patch 'params' value - result, err := ctx.Codec.MarshalJSON(&srcConGen) - if err != nil { - return nil, err - } - genState := map[string]json.RawMessage{} - if err := json.Unmarshal(result, &genState); err != nil { - return nil, fmt.Errorf("unmarshalling 'GenesisState' failed: %v", err) - } - genState["params"] = params - - result, err = json.Marshal(genState) - if err != nil { - return nil, fmt.Errorf("marshalling transformation result failed: %v", err) - } - return result, nil + return json.Marshal(paramsMap) } -// Transformation of consumer genesis content as it is exported from current provider version -// to a format readable by consumer implementation of version v2.x -// Use removePreHashKey to remove prehash_key_before_comparison from result. -func transformToV2(jsonRaw []byte, ctx client.Context, removePreHashKey bool) (json.RawMessage, error) { - // populate deprecated fields of GenesisState used by version v2.x - srcConGen := consumerTypes.GenesisState{} +// Transformation of consumer genesis content as it is exported by provider version >= v6.2.x +// to a format supported by consumer chains version with either SDK v0.47 and ICS < v4.5.0 or SDK v0.50 and ICS < v6.2.0 +// This transformation removes the 'consumer_id' parameter from the 'params' field introduced in ICS v6.2.x +func transformToV5(jsonRaw []byte, ctx client.Context) (json.RawMessage, error) { + srcConGen := types.ConsumerGenesisState{} err := ctx.Codec.UnmarshalJSON(jsonRaw, &srcConGen) if err != nil { return nil, fmt.Errorf("reading consumer genesis data failed: %s", err) } - // remove retry_delay_period from 'params' if present (introduced in v4.x) + // Remove 'consumer_id' from 'params' params, err := ctx.Codec.MarshalJSON(&srcConGen.Params) if err != nil { return nil, err } - paramsMap := map[string]json.RawMessage{} - if err := json.Unmarshal(params, ¶msMap); err != nil { - return nil, fmt.Errorf("unmarshalling 'params' failed: %v", err) - } - _, exists := paramsMap["retry_delay_period"] - if exists { - delete(paramsMap, "retry_delay_period") - } - params, err = json.Marshal(paramsMap) + + params, err = removeParameterFromParams(params, "consumer_id") if err != nil { return nil, err } - // marshal GenesisState and patch 'params' value + // Marshal GenesisState and patch 'params' value result, err := ctx.Codec.MarshalJSON(&srcConGen) if err != nil { return nil, err @@ -189,65 +85,10 @@ func transformToV2(jsonRaw []byte, ctx client.Context, removePreHashKey bool) (j } genState["params"] = params - provider, err := ctx.Codec.MarshalJSON(&srcConGen.Provider) - if err != nil { - return nil, fmt.Errorf("marshalling 'Provider' failed: %v", err) - } - providerMap := map[string]json.RawMessage{} - if err := json.Unmarshal(provider, &providerMap); err != nil { - return nil, fmt.Errorf("unmarshalling 'provider' failed: %v", err) - } - - // patch .initial_val_set form .provider.initial_val_set if needed - if len(srcConGen.Provider.InitialValSet) > 0 { - valSet, exists := providerMap["initial_val_set"] - if !exists { - return nil, fmt.Errorf("'initial_val_set' not found in provider data") - } - _, exists = genState["initial_val_set"] - if exists { - genState["initial_val_set"] = valSet - } - } - - // patch .provider_consensus_state from provider.consensus_state if needed - if srcConGen.Provider.ConsensusState != nil { - valSet, exists := providerMap["consensus_state"] - if !exists { - return nil, fmt.Errorf("'consensus_state' not found in provider data") - } - _, exists = genState["provider_consensus_state"] - if exists { - genState["provider_consensus_state"] = valSet - } - } - - // patch .provider_client_state from provider.client_state if needed - if srcConGen.Provider.ClientState != nil { - clientState, exists := providerMap["client_state"] - if !exists { - return nil, fmt.Errorf("'client_state' not found in provider data") - } - _, exists = genState["provider_client_state"] - if exists { - genState["provider_client_state"] = clientState - } - } - - // delete .provider entry (introduced in v3.3.x) - delete(genState, "provider") - - // Marshall final result result, err = json.Marshal(genState) if err != nil { return nil, fmt.Errorf("marshalling transformation result failed: %v", err) } - - if removePreHashKey { - // remove all `prehash_key_before_comparison` entries not supported in v2.x (see ics23) - re := regexp.MustCompile(`,\s*"prehash_key_before_comparison"\s*:\s*(false|true)`) - result = re.ReplaceAll(result, []byte{}) - } return result, nil } @@ -258,16 +99,8 @@ func transformGenesis(ctx client.Context, targetVersion IcsVersion, jsonRaw []by var err error switch targetVersion { - // v2.x, v3.0-v3.2 share same consumer genesis type - case v2_x: - newConsumerGenesis, err = transformToV2(jsonRaw, ctx, true) - case v3_0_x, v3_1_x, v3_2_x: - // same as v2 replacement without need of `prehash_key_before_comparison` removal - newConsumerGenesis, err = transformToV2(jsonRaw, ctx, false) - case v3_3_x: - newConsumerGenesis, err = transformToV33(jsonRaw, ctx) - case v4_x_x: - newConsumerGenesis, err = transformToNew(jsonRaw, ctx) + case v4_x_x, v5_x_x: + newConsumerGenesis, err = transformToV5(jsonRaw, ctx) default: err = fmt.Errorf("unsupported target version '%s'. Run %s --help", targetVersion, version.AppName) @@ -280,10 +113,9 @@ func transformGenesis(ctx client.Context, targetVersion IcsVersion, jsonRaw []by } // Transform a consumer genesis json file exported from a given ccv provider version -// to a consumer genesis json format supported by current ccv consumer version or v2.x +// to a consumer genesis json format supported by current ccv consumer version // This allows user to patch consumer genesis of -// - current implementation from exports of provider of < v3.3.x -// - v2.x from exports of provider >= v3.2.x +// - v4.x, v5.x, v6.1.x from exports of provider >= v6.2.x // // Result will be written to defined output. func TransformConsumerGenesis(cmd *cobra.Command, args []string) error { @@ -336,7 +168,7 @@ func GetConsumerGenesisTransformCmd() *cobra.Command { Short: "Transform CCV consumer genesis data exported to a specific target format", Long: strings.TrimSpace( fmt.Sprintf(` -Transform the consumer genesis data exported from a provider version v1,v2, v3, v4 to a specified consumer target version. +Transform the consumer genesis data exported from a provider version v5.x v6.x to a specified consumer target version. The result is printed to STDOUT. Note: Content to be transformed is not the consumer genesis file itself but the exported content from provider chain which is used to patch the consumer genesis file! @@ -349,7 +181,7 @@ $ %s --to v2.x transform /path/to/ccv_consumer_genesis.json Args: cobra.RangeArgs(1, 2), RunE: TransformConsumerGenesis, } - cmd.Flags().String("to", string(v4_x_x), + cmd.Flags().String("to", string(v5_x_x), fmt.Sprintf("target version for consumer genesis. Supported versions %s", maps.Keys(TransformationVersions))) return cmd diff --git a/app/consumer/genesis_test.go b/app/consumer/genesis_test.go index 144dbd74c9..8c9266f626 100644 --- a/app/consumer/genesis_test.go +++ b/app/consumer/genesis_test.go @@ -3,12 +3,13 @@ package app_test import ( "bytes" "context" + "encoding/json" "fmt" "io/fs" "os" "path/filepath" + "reflect" "testing" - "time" "github.com/spf13/cobra" "github.com/stretchr/testify/require" @@ -17,382 +18,139 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" app "github.com/cosmos/interchain-security/v6/app/consumer" - consumerTypes "github.com/cosmos/interchain-security/v6/x/ccv/consumer/types" ccvtypes "github.com/cosmos/interchain-security/v6/x/ccv/types" ) const ( - V4x = "v4.x" - V33x = "v3.3.x" - V2x = "v2.x" + V6x = "v6.x" + V5x = "v5.x" + V4x = "v4.x" ) // Testdata mapping consumer genesis exports to a provider module version as // used by transformation function for consumer genesis content. var consumerGenesisStates map[string]string = map[string]string{ - "v2.x": ` + "v6.x": ` { - "params": { - "enabled": true, - "blocks_per_distribution_transmission": "1500", - "distribution_transmission_channel": "", - "provider_fee_pool_addr_str": "", - "ccv_timeout_period": "2419200s", - "transfer_timeout_period": "3600s", - "consumer_redistribution_fraction": "0.75", - "historical_entries": "10000", - "unbonding_period": "1728000s", - "soft_opt_out_threshold": "", - "reward_denoms": [], - "provider_reward_denoms": [] - }, - "provider_client_id": "", - "provider_channel_id": "", - "new_chain": true, - "provider_client_state": { - "chain_id": "cosmoshub-4", - "trust_level": { - "numerator": "1", - "denominator": "3" - }, - "trusting_period": "1197504s", - "unbonding_period": "1814400s", - "max_clock_drift": "10s", - "frozen_height": { - "revision_number": "0", - "revision_height": "0" - }, - "latest_height": { - "revision_number": "4", - "revision_height": "15211521" - }, - "proof_specs": [ - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 33, - "min_prefix_length": 4, - "max_prefix_length": 12, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0 - }, - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 32, - "min_prefix_length": 1, - "max_prefix_length": 1, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0 - } - ], - "upgrade_path": [ - "upgrade", - "upgradedIBCState" - ], - "allow_update_after_expiry": true, - "allow_update_after_misbehaviour": true - }, - "provider_consensus_state": { - "timestamp": "2023-05-08T11:00:01.563901871Z", - "root": { - "hash": "qKVnVSXlsjDHC8ekKcy/0zSjzr3YekCurld9R4W07EI=" - }, - "next_validators_hash": "E08978F493101A3C5D459FB3087B8CFBA9E82D7A1FE1441E7D77E11AC0586BAC" - }, - "maturing_packets": [], - "initial_val_set": [ - { - "pub_key": { - "ed25519": "cOQZvh/h9ZioSeUMZB/1Vy1Xo5x2sjrVjlE/qHnYifM=" - }, - "power": "2345194" - }, - { - "pub_key": { - "ed25519": "vGSKfbQyKApvBhinpOOA0XQAdjxceihYNwtGskfZGyQ=" - }, - "power": "463811" - } - ], - "height_to_valset_update_id": [], - "outstanding_downtime_slashing": [], - "pending_consumer_packets": { - "list": [] - }, - "last_transmission_block_height": { - "height": "0" - }, - "preCCV": false - } - - `, - "v3.3.x": ` - { - "params": { - "enabled": true, - "blocks_per_distribution_transmission": "1000", - "distribution_transmission_channel": "", - "provider_fee_pool_addr_str": "", - "ccv_timeout_period": "2419200s", - "transfer_timeout_period": "3600s", - "consumer_redistribution_fraction": "0.75", - "historical_entries": "10000", - "unbonding_period": "1209600s", - "soft_opt_out_threshold": "0.05", - "reward_denoms": [], - "provider_reward_denoms": [] - }, - "provider": { - "client_state": { - "chain_id": "provi", - "trust_level": { - "numerator": "1", - "denominator": "3" - }, - "trusting_period": "1197504s", - "unbonding_period": "1814400s", - "max_clock_drift": "10s", - "frozen_height": { - "revision_number": "0", - "revision_height": "0" - }, - "latest_height": { - "revision_number": "0", - "revision_height": "20" - }, - "proof_specs": [ - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 33, - "min_prefix_length": 4, - "max_prefix_length": 12, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0, - "prehash_key_before_comparison": false - }, - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 32, - "min_prefix_length": 1, - "max_prefix_length": 1, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0, - "prehash_key_before_comparison": false - } - ], - "upgrade_path": [ - "upgrade", - "upgradedIBCState" - ], - "allow_update_after_expiry": false, - "allow_update_after_misbehaviour": false - }, - "consensus_state": { - "timestamp": "2023-12-15T09:25:46.098392003Z", - "root": { - "hash": "0aoNOwWy67aQKs2r+FcDf2RxIq2UJtBb3g9ZWn0Gkas=" - }, - "next_validators_hash": "632730A03DEF630F77B61DF4092629007AE020B789713158FABCB104962FA54F" - }, - "initial_val_set": [ - { - "pub_key": { - "ed25519": "RrclQz9bIhkIy/gfL485g3PYMeiIku4qeo495787X10=" - }, - "power": "500" - }, - { - "pub_key": { - "ed25519": "Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=" - }, - "power": "500" - }, - { - "pub_key": { - "ed25519": "mAN6RXYxSM4MNGSIriYiS7pHuwAcOHDQAy9/wnlSzOI=" - }, - "power": "500" - } - ] - }, - "new_chain": true - } - `, - "v4.x": ` - { - "params": { - "enabled": true, - "blocks_per_distribution_transmission": "1000", - "distribution_transmission_channel": "", - "provider_fee_pool_addr_str": "", - "ccv_timeout_period": "2419200s", - "transfer_timeout_period": "3600s", - "consumer_redistribution_fraction": "0.75", - "historical_entries": "10000", - "unbonding_period": "1209600s", - "soft_opt_out_threshold": "0.05", - "reward_denoms": [], - "provider_reward_denoms": [], - "retry_delay_period": "3600s" - }, - "provider": { - "client_state": { - "chain_id": "provi", - "trust_level": { - "numerator": "1", - "denominator": "3" - }, - "trusting_period": "1197504s", - "unbonding_period": "1814400s", - "max_clock_drift": "10s", - "frozen_height": { - "revision_number": "0", - "revision_height": "0" - }, - "latest_height": { - "revision_number": "0", - "revision_height": "20" - }, - "proof_specs": [ - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 33, - "min_prefix_length": 4, - "max_prefix_length": 12, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0, - "prehash_key_before_comparison": false - }, - { - "leaf_spec": { - "hash": "SHA256", - "prehash_key": "NO_HASH", - "prehash_value": "SHA256", - "length": "VAR_PROTO", - "prefix": "AA==" - }, - "inner_spec": { - "child_order": [ - 0, - 1 - ], - "child_size": 32, - "min_prefix_length": 1, - "max_prefix_length": 1, - "empty_child": null, - "hash": "SHA256" - }, - "max_depth": 0, - "min_depth": 0, - "prehash_key_before_comparison": false - } - ], - "upgrade_path": [ - "upgrade", - "upgradedIBCState" - ], - "allow_update_after_expiry": false, - "allow_update_after_misbehaviour": false - }, - "consensus_state": { - "timestamp": "2023-12-15T09:57:02.687079137Z", - "root": { - "hash": "EH9YbrWC3Qojy8ycl5GhOdVEC1ifPIGUUItL70bTkHo=" - }, - "next_validators_hash": "632730A03DEF630F77B61DF4092629007AE020B789713158FABCB104962FA54F" - }, - "initial_val_set": [ - { - "pub_key": { - "ed25519": "RrclQz9bIhkIy/gfL485g3PYMeiIku4qeo495787X10=" - }, - "power": "500" - }, - { - "pub_key": { - "ed25519": "Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=" - }, - "power": "500" - }, - { - "pub_key": { - "ed25519": "mAN6RXYxSM4MNGSIriYiS7pHuwAcOHDQAy9/wnlSzOI=" - }, - "power": "500" - } - ] - }, - "new_chain": true - } + "params": { + "enabled": true, + "blocks_per_distribution_transmission": "1000", + "distribution_transmission_channel": "", + "provider_fee_pool_addr_str": "", + "ccv_timeout_period": "2419200s", + "transfer_timeout_period": "3600s", + "consumer_redistribution_fraction": "0.75", + "historical_entries": "10000", + "unbonding_period": "1209600s", + "soft_opt_out_threshold": "0", + "reward_denoms": [], + "provider_reward_denoms": [], + "retry_delay_period": "3600s", + "consumer_id": "1" + }, + "provider": { + "client_state": { + "chain_id": "provi", + "trust_level": { + "numerator": "1", + "denominator": "3" + }, + "trusting_period": "1197504s", + "unbonding_period": "1814400s", + "max_clock_drift": "10s", + "frozen_height": { + "revision_number": "0", + "revision_height": "0" + }, + "latest_height": { + "revision_number": "0", + "revision_height": "24" + }, + "proof_specs": [ + { + "leaf_spec": { + "hash": "SHA256", + "prehash_key": "NO_HASH", + "prehash_value": "SHA256", + "length": "VAR_PROTO", + "prefix": "AA==" + }, + "inner_spec": { + "child_order": [ + 0, + 1 + ], + "child_size": 33, + "min_prefix_length": 4, + "max_prefix_length": 12, + "empty_child": null, + "hash": "SHA256" + }, + "max_depth": 0, + "min_depth": 0, + "prehash_key_before_comparison": false + }, + { + "leaf_spec": { + "hash": "SHA256", + "prehash_key": "NO_HASH", + "prehash_value": "SHA256", + "length": "VAR_PROTO", + "prefix": "AA==" + }, + "inner_spec": { + "child_order": [ + 0, + 1 + ], + "child_size": 32, + "min_prefix_length": 1, + "max_prefix_length": 1, + "empty_child": null, + "hash": "SHA256" + }, + "max_depth": 0, + "min_depth": 0, + "prehash_key_before_comparison": false + } + ], + "upgrade_path": [ + "upgrade", + "upgradedIBCState" + ], + "allow_update_after_expiry": false, + "allow_update_after_misbehaviour": false + }, + "consensus_state": { + "timestamp": "2024-10-17T07:47:33.124389629Z", + "root": { + "hash": "cgIJagBEc/5lDkWS12NG5i7SSZ5hNFlDrlparFaWytc=" + }, + "next_validators_hash": "632730A03DEF630F77B61DF4092629007AE020B789713158FABCB104962FA54F" + }, + "initial_val_set": [ + { + "pub_key": { + "ed25519": "RrclQz9bIhkIy/gfL485g3PYMeiIku4qeo495787X10=" + }, + "power": "500" + }, + { + "pub_key": { + "ed25519": "Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=" + }, + "power": "500" + }, + { + "pub_key": { + "ed25519": "mAN6RXYxSM4MNGSIriYiS7pHuwAcOHDQAy9/wnlSzOI=" + }, + "power": "500" + } + ] + }, + "new_chain": true + } `, } @@ -458,223 +216,84 @@ func transformConsumerGenesis(filePath string, version *string) ([]byte, error) return result.Bytes(), nil } -// Check transformation of a version 2 ConsumerGenesis export to -// consumer genesis json format used by current consumer implementation. -func TestConsumerGenesisTransformationFromV2ToCurrent(t *testing.T) { - version := V2x - ctx := getClientCtx() - - srcGenesis := consumerTypes.GenesisState{} - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) - require.NoError(t, err, "Error parsing old version of ccv genesis content for consumer") - - filePath := createConsumerDataGenesisFile(t, version) - defer os.Remove(filePath) - resultGenesis := consumerTypes.GenesisState{} - result, err := transformConsumerGenesis(filePath, nil) - require.NoError(t, err) - err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) - require.NoError(t, err) - - // Some basic sanity checks on the content. - require.NotNil(t, resultGenesis.Provider.ClientState) - require.Equal(t, "cosmoshub-4", resultGenesis.Provider.ClientState.ChainId) - - require.Empty(t, resultGenesis.InitialValSet) - require.NotEmpty(t, resultGenesis.Provider.InitialValSet) - require.Equal(t, resultGenesis.Params.RetryDelayPeriod, ccvtypes.DefaultRetryDelayPeriod) - - // Check params: retry_delay_period prevents direct comparison - require.EqualValues(t, srcGenesis.Params.Enabled, resultGenesis.Params.Enabled) - require.EqualValues(t, srcGenesis.Params.BlocksPerDistributionTransmission, resultGenesis.Params.BlocksPerDistributionTransmission) - require.EqualValues(t, srcGenesis.Params.DistributionTransmissionChannel, resultGenesis.Params.DistributionTransmissionChannel) - require.EqualValues(t, srcGenesis.Params.ProviderFeePoolAddrStr, resultGenesis.Params.ProviderFeePoolAddrStr) - require.EqualValues(t, srcGenesis.Params.CcvTimeoutPeriod, resultGenesis.Params.CcvTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.TransferTimeoutPeriod, resultGenesis.Params.TransferTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.ConsumerRedistributionFraction, resultGenesis.Params.ConsumerRedistributionFraction) - require.EqualValues(t, srcGenesis.Params.HistoricalEntries, resultGenesis.Params.HistoricalEntries) - require.EqualValues(t, srcGenesis.Params.UnbondingPeriod, resultGenesis.Params.UnbondingPeriod) - - // `SoftOptOutThreshold` is deprecated, so it should be set to zero the current version - require.EqualValues(t, "0", resultGenesis.Params.SoftOptOutThreshold) - require.EqualValues(t, srcGenesis.Params.RewardDenoms, resultGenesis.Params.RewardDenoms) - require.EqualValues(t, srcGenesis.Params.ProviderRewardDenoms, resultGenesis.Params.ProviderRewardDenoms) - - require.Equal(t, srcGenesis.ProviderClientState, resultGenesis.Provider.ClientState) - require.Nil(t, resultGenesis.ProviderClientState) - - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.ProviderConsensusState) - require.Nil(t, resultGenesis.ProviderConsensusState) - - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) - require.Equal(t, srcGenesis.InitialValSet, resultGenesis.Provider.InitialValSet) - require.Empty(t, resultGenesis.InitialValSet) -} - -// Check transformation of provider v3.3.x implementation to consumer V2 -func TestConsumerGenesisTransformationV330ToV2(t *testing.T) { - version := V33x - filePath := createConsumerDataGenesisFile(t, version) - defer os.Remove(filePath) - - var srcGenesis consumerTypes.GenesisState - ctx := getClientCtx() - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) - require.NoError(t, err) - - targetVersion := V2x - result, err := transformConsumerGenesis(filePath, &targetVersion) - require.NoError(t, err) - - resultGenesis := consumerTypes.GenesisState{} - err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) - require.NoError(t, err) - - require.Equal(t, srcGenesis.Params, resultGenesis.Params) - require.Equal(t, srcGenesis.Provider.ClientState, resultGenesis.ProviderClientState) - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.ProviderConsensusState) - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) +// Check transformation of provider v6.2.x implementation to consumer v5.x +func TestConsumerGenesisTransformationV6ToV5(t *testing.T) { + CheckGenesisTransform(t, V6x, V5x) } -// Check transformation of provider v3.3.x implementation to current consumer version -func TestConsumerGenesisTransformationV330ToCurrent(t *testing.T) { - version := V33x - filePath := createConsumerDataGenesisFile(t, version) - defer os.Remove(filePath) - - var srcGenesis consumerTypes.GenesisState - ctx := getClientCtx() - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) - require.NoError(t, err) - - result, err := transformConsumerGenesis(filePath, nil) - require.NoError(t, err) - - resultGenesis := consumerTypes.GenesisState{} - err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) - require.NoError(t, err) - - require.Equal(t, srcGenesis.Params.Enabled, resultGenesis.Params.Enabled) - require.Equal(t, srcGenesis.Params.BlocksPerDistributionTransmission, resultGenesis.Params.BlocksPerDistributionTransmission) - require.Equal(t, srcGenesis.Params.DistributionTransmissionChannel, resultGenesis.Params.DistributionTransmissionChannel) - require.Equal(t, srcGenesis.Params.ProviderFeePoolAddrStr, resultGenesis.Params.ProviderFeePoolAddrStr) - require.Equal(t, srcGenesis.Params.CcvTimeoutPeriod, resultGenesis.Params.CcvTimeoutPeriod) - require.Equal(t, srcGenesis.Params.TransferTimeoutPeriod, resultGenesis.Params.TransferTimeoutPeriod) - require.Equal(t, srcGenesis.Params.ConsumerRedistributionFraction, resultGenesis.Params.ConsumerRedistributionFraction) - require.Equal(t, srcGenesis.Params.HistoricalEntries, resultGenesis.Params.HistoricalEntries) - require.Equal(t, srcGenesis.Params.UnbondingPeriod, resultGenesis.Params.UnbondingPeriod) - - // `SoftOptOutThreshold` is deprecated, so it should be set to zero the current version - require.Equal(t, "0", resultGenesis.Params.SoftOptOutThreshold) - - require.Equal(t, srcGenesis.Params.RewardDenoms, resultGenesis.Params.RewardDenoms) - require.Equal(t, srcGenesis.Params.ProviderRewardDenoms, resultGenesis.Params.ProviderRewardDenoms) - - require.Equal(t, resultGenesis.Params.RetryDelayPeriod, ccvtypes.DefaultRetryDelayPeriod) - - require.Equal(t, srcGenesis.Provider.ClientState, resultGenesis.Provider.ClientState) - require.Nil(t, resultGenesis.ProviderClientState) - require.Nil(t, resultGenesis.ProviderConsensusState) - - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.Provider.ConsensusState) - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) +// Check transformation of provider v6.2.x implementation to consumer v4.x +func TestConsumerGenesisTransformationV6ToV4(t *testing.T) { + CheckGenesisTransform(t, V6x, V4x) } -// Check transformation of provider v4.x implementation to consumer V2 -func TestConsumerGenesisTransformationV4ToV2(t *testing.T) { - version := V4x - filePath := createConsumerDataGenesisFile(t, version) +// CheckGenesisTransform checks that the transformation of consumer genesis data +// from a given source version to a target version is successful +func CheckGenesisTransform(t *testing.T, sourceVersion string, targetVersion string) { + filePath := createConsumerDataGenesisFile(t, sourceVersion) defer os.Remove(filePath) - var srcGenesis consumerTypes.GenesisState + var srcGenesis ccvtypes.ConsumerGenesisState ctx := getClientCtx() - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) + err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[sourceVersion]), &srcGenesis) require.NoError(t, err) - targetVersion := V2x result, err := transformConsumerGenesis(filePath, &targetVersion) require.NoError(t, err) - resultGenesis := consumerTypes.GenesisState{} + resultGenesis := ccvtypes.ConsumerGenesisState{} err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) require.NoError(t, err) - // Check params: retry_delay_period prevents direct comparison - require.EqualValues(t, srcGenesis.Params.Enabled, resultGenesis.Params.Enabled) - require.EqualValues(t, srcGenesis.Params.BlocksPerDistributionTransmission, resultGenesis.Params.BlocksPerDistributionTransmission) - require.EqualValues(t, srcGenesis.Params.DistributionTransmissionChannel, resultGenesis.Params.DistributionTransmissionChannel) - require.EqualValues(t, srcGenesis.Params.ProviderFeePoolAddrStr, resultGenesis.Params.ProviderFeePoolAddrStr) - require.EqualValues(t, srcGenesis.Params.CcvTimeoutPeriod, resultGenesis.Params.CcvTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.TransferTimeoutPeriod, resultGenesis.Params.TransferTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.ConsumerRedistributionFraction, resultGenesis.Params.ConsumerRedistributionFraction) - require.EqualValues(t, srcGenesis.Params.HistoricalEntries, resultGenesis.Params.HistoricalEntries) - require.EqualValues(t, srcGenesis.Params.UnbondingPeriod, resultGenesis.Params.UnbondingPeriod) - require.EqualValues(t, srcGenesis.Params.SoftOptOutThreshold, resultGenesis.Params.SoftOptOutThreshold) - require.EqualValues(t, srcGenesis.Params.RewardDenoms, resultGenesis.Params.RewardDenoms) - require.EqualValues(t, srcGenesis.Params.ProviderRewardDenoms, resultGenesis.Params.ProviderRewardDenoms) - require.Equal(t, resultGenesis.Params.RetryDelayPeriod, time.Duration(0)) - - require.Equal(t, srcGenesis.Provider.ClientState, resultGenesis.ProviderClientState) - require.Nil(t, resultGenesis.Provider.ClientState) - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.ProviderConsensusState) - require.Nil(t, resultGenesis.Provider.ConsensusState) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) - - require.Equal(t, 0, len(resultGenesis.Provider.InitialValSet)) - require.Equal(t, srcGenesis.Provider.InitialValSet, resultGenesis.InitialValSet) - require.Empty(t, resultGenesis.Provider.InitialValSet) - - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) -} - -// Check transformation of provider v3.3.x implementation to consumer V2 -func TestConsumerGenesisTransformationV4ToV33(t *testing.T) { - version := V4x - filePath := createConsumerDataGenesisFile(t, version) - defer os.Remove(filePath) - - var srcGenesis ccvtypes.ConsumerGenesisState - ctx := getClientCtx() - err := ctx.Codec.UnmarshalJSON([]byte(consumerGenesisStates[version]), &srcGenesis) + resultRaw := map[string]json.RawMessage{} + err = json.Unmarshal(result, &resultRaw) require.NoError(t, err) - targetVersion := V33x - result, err := transformConsumerGenesis(filePath, &targetVersion) - require.NoError(t, err) - resultGenesis := consumerTypes.GenesisState{} // Only difference to v33 is no RetryDelayPeriod - err = ctx.Codec.UnmarshalJSON(result, &resultGenesis) - require.NoError(t, err) + // Check that resultRaw has no subelement consumer_id + paramsRaw, found := resultRaw["params"] + require.True(t, found, "params field not found in result genesis") - // Check params: retry_delay_period prevents direct comparison - require.EqualValues(t, srcGenesis.Params.Enabled, resultGenesis.Params.Enabled) - require.EqualValues(t, srcGenesis.Params.BlocksPerDistributionTransmission, resultGenesis.Params.BlocksPerDistributionTransmission) - require.EqualValues(t, srcGenesis.Params.DistributionTransmissionChannel, resultGenesis.Params.DistributionTransmissionChannel) - require.EqualValues(t, srcGenesis.Params.ProviderFeePoolAddrStr, resultGenesis.Params.ProviderFeePoolAddrStr) - require.EqualValues(t, srcGenesis.Params.CcvTimeoutPeriod, resultGenesis.Params.CcvTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.TransferTimeoutPeriod, resultGenesis.Params.TransferTimeoutPeriod) - require.EqualValues(t, srcGenesis.Params.ConsumerRedistributionFraction, resultGenesis.Params.ConsumerRedistributionFraction) - require.EqualValues(t, srcGenesis.Params.HistoricalEntries, resultGenesis.Params.HistoricalEntries) - require.EqualValues(t, srcGenesis.Params.UnbondingPeriod, resultGenesis.Params.UnbondingPeriod) - require.EqualValues(t, srcGenesis.Params.SoftOptOutThreshold, resultGenesis.Params.SoftOptOutThreshold) - require.EqualValues(t, srcGenesis.Params.RewardDenoms, resultGenesis.Params.RewardDenoms) - require.EqualValues(t, srcGenesis.Params.ProviderRewardDenoms, resultGenesis.Params.ProviderRewardDenoms) - - require.Equal(t, srcGenesis.Provider.ClientState, resultGenesis.Provider.ClientState) - require.Nil(t, resultGenesis.ProviderClientState) + params := map[string]json.RawMessage{} + err = json.Unmarshal(paramsRaw, ¶ms) + require.NoError(t, err) - require.Equal(t, srcGenesis.Provider.ConsensusState, resultGenesis.Provider.ConsensusState) - require.Nil(t, resultGenesis.ProviderConsensusState) + _, consumerIdFound := params["consumer_id"] + require.False(t, consumerIdFound, "consumer_id field should not be present in params") + + // Iterate over all fields of ConsumerParams and check: + // - that they match between source and result genesis + // - that ConsumerId is empty in the result genesis + srcParams := reflect.ValueOf(srcGenesis.Params) + resultParams := reflect.ValueOf(resultGenesis.Params) + srcType := srcParams.Type() + for i := 0; i < srcParams.NumField(); i++ { + fieldName := srcType.Field(i).Name + srcField := srcParams.Field(i).Interface() + // srcType and resultType are the same so we can use index 'i' to get the field from resultParams + resultField := resultParams.Field(i).Interface() + if fieldName == "ConsumerId" { + // ConsumerId is not present in v5.x => expect empty string when unmarshalled to v6 + require.EqualValues(t, "", resultField, "Field %s does not match", fieldName) + } else { + require.EqualValues(t, srcField, resultField, "Field %s does not match", fieldName) + } + } - require.Equal(t, srcGenesis.NewChain, resultGenesis.NewChain) - require.Equal(t, "", resultGenesis.ProviderClientId) - require.Equal(t, "", resultGenesis.ProviderChannelId) - require.Equal(t, srcGenesis.Provider.InitialValSet, resultGenesis.Provider.InitialValSet) - require.Empty(t, resultGenesis.InitialValSet) + // Iterate over all fields of ConsumerGenesisState and check + // - that they match between source and result genesis + // - that ConsumerId is empty in the result genesis + srcParams = reflect.ValueOf(srcGenesis) + resultParams = reflect.ValueOf(resultGenesis) + srcType = srcParams.Type() + require.Equal(t, srcParams.Type(), resultParams.Type(), "Different types of source and result genesis") + for i := 0; i < srcParams.NumField(); i++ { + fieldName := srcType.Field(i).Name + // Skip Params field as it was checked above + if fieldName == "Params" { + continue + } + srcField := srcParams.Field(i).Interface() + // srcType and resultType are the same so we can use index 'i' to get the field from resultParams + resultField := resultParams.Field(i).Interface() + require.EqualValues(t, srcField, resultField, "Field %s does not match", fieldName) + } }