From ff578dadd527ce09a47a871872a2e2a8b9d4521c Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 7 Mar 2024 16:19:51 +0100 Subject: [PATCH] feat!: introduce epochs (#1660) * cleanup ./changelog entries * rebase * fix!: Validation of SlashAcks fails due to marshaling to Bech32 (backport #1570) (#1577) fix!: Validation of SlashAcks fails due to marshaling to Bech32 (#1570) * add different Bech32Prefix for consumer and provider * separate app encoding and params * remove ConsumerValPubKey from ValidatorConfig * update addresses in tests * make SlashAcks consistent across chains * add comments for clarity * Regenerate traces * Fix argument order * set bech32prefix for provider to cosmos * add changelog entries * add consumer-double-downtime e2e test * update nightly-e2e workflow * fix typo * add consumer-double-downtime to testConfigs * remove changes on provider * skip invalid SlashAcks * seal the config * clear the outstanding downtime flag for new vals * add info on upgrading to v4.0.0 * fix upgrade handler * fix changeover e2e test * Update tests/e2e/config.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update tests/e2e/config.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * add AccountPrefix to ChainConfig * fix docstrings * update AccountAddressPrefix in app.go * fix consumer-misb e2e test --------- Co-authored-by: Philip Offtermatt Co-authored-by: Simon Noetzlin Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> (cherry picked from commit 86046926502f7b0ba795bebcdd1fdc97ac776573) Co-authored-by: Marius Poke * docs: update changelog for v4.0.0 (#1578) update changelog * docs: prepare for v4.0.0 (#1581) * unclog build * update release notes * update release date * added proto declaration * temp commit * temp commit * more changes * first commit * add param and fix tests * reduce epoch size for e2e * clean up * mbt fix * fix diff bug * cleaning up * cleaning up * cleaning up * cleaning up * cleaning up * cleaning up * added more tests * more fixes * nit fixes * cleaning up * increase downtime by one block * fix logs * took into account Marius' comments * tiny fixes * Update x/ccv/provider/keeper/params.go Co-authored-by: Simon Noetzlin * use Bech32 addresses as keys for maps * refactor nextBlocks(epoch) to nextEpoch * fixed comment * Remove new block creation during consumer chain setup * Revert "Remove new block creation during consumer chain setup" This reverts commit 85a52b74c1998dfebadfedbf287c9c9547cfec78. * added simple param test * added upper bound and addressed a comment * Add another edge case for diffing * used smarted solution (based on Philip's comment) for diffing validators * refactor!: remove key-assignment replacements (#1672) * initial commit * removed KeyAssignmentReplacementsKey * refactor: simplify key-assignment logic (#1684) * fixed typo: depreciated to deprecated --------- Co-authored-by: Marius Poke * add the epoch param in the docs --------- Co-authored-by: mpoke Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Simon Noetzlin Co-authored-by: Philip Offtermatt --- docs/docs/adrs/adr-001-key-assignment.md | 116 +--- docs/docs/introduction/params.md | 7 + .../ccv/provider/v1/provider.proto | 14 + tests/e2e/actions.go | 14 +- tests/e2e/config.go | 33 +- tests/integration/common.go | 11 +- tests/integration/distribution.go | 6 +- tests/integration/expired_client.go | 16 +- tests/integration/key_assignment.go | 18 +- tests/integration/setup.go | 6 + tests/integration/slashing.go | 4 +- tests/integration/soft_opt_out.go | 6 +- tests/integration/unbonding.go | 11 +- tests/integration/valset_update.go | 4 +- tests/mbt/driver/mbt_test.go | 20 +- tests/mbt/driver/setup.go | 20 + testutil/keeper/unit_test_helpers.go | 1 - x/ccv/consumer/types/keys.go | 4 +- x/ccv/provider/keeper/grpc_query_test.go | 1 - x/ccv/provider/keeper/key_assignment.go | 244 +------- x/ccv/provider/keeper/key_assignment_test.go | 105 +--- x/ccv/provider/keeper/params.go | 8 + x/ccv/provider/keeper/params_test.go | 1 + x/ccv/provider/keeper/proposal.go | 12 +- x/ccv/provider/keeper/proposal_test.go | 1 + x/ccv/provider/keeper/relay.go | 29 +- x/ccv/provider/keeper/relay_test.go | 60 +- x/ccv/provider/keeper/validator_set_update.go | 178 ++++++ .../keeper/validator_set_update_test.go | 355 ++++++++++++ x/ccv/provider/types/genesis_test.go | 26 +- x/ccv/provider/types/keys.go | 19 +- x/ccv/provider/types/keys_test.go | 2 +- x/ccv/provider/types/params.go | 30 + x/ccv/provider/types/params_test.go | 33 +- x/ccv/provider/types/provider.pb.go | 526 ++++++++++++++---- 35 files changed, 1313 insertions(+), 628 deletions(-) create mode 100644 x/ccv/provider/keeper/validator_set_update.go create mode 100644 x/ccv/provider/keeper/validator_set_update_test.go diff --git a/docs/docs/adrs/adr-001-key-assignment.md b/docs/docs/adrs/adr-001-key-assignment.md index 874321db0c..36dbdfdb09 100644 --- a/docs/docs/adrs/adr-001-key-assignment.md +++ b/docs/docs/adrs/adr-001-key-assignment.md @@ -7,6 +7,7 @@ title: Key Assignment ## Changelog * 2022-12-01: Initial Draft +* 2024-03-01: Updated to take into account they key-assigment-replacement deprecation. ## Status @@ -30,10 +31,6 @@ ConsumerValidatorsBytePrefix | len(chainID) | chainID | providerConsAddress -> c ```golang ValidatorsByConsumerAddrBytePrefix | len(chainID) | chainID | consumerConsAddress -> providerConsAddress ``` -- `KeyAssignmentReplacements` - Stores the key assignments that need to be replaced in the current block. Needed to apply the key assignments received in a block to the validator updates sent to the consumer chains. -```golang -KeyAssignmentReplacementsBytePrefix | len(chainID) | chainID | providerConsAddress -> abci.ValidatorUpdate{PubKey: oldConsumerKey, Power: currentPower}, -``` - `ConsumerAddrsToPrune` - Stores the mapping from VSC ids to consumer validators addresses. Needed for pruning `ValidatorByConsumerAddr`. ```golang ConsumerAddrsToPruneBytePrefix | len(chainID) | chainID | vscID -> []consumerConsAddresses @@ -67,20 +64,6 @@ if _, consumerRegistered := GetConsumerClientId(chainID); consumerRegistered { oldConsumerAddr := utils.TMCryptoPublicKeyToConsAddr(oldConsumerKey) vscID := GetValidatorSetUpdateId() AppendConsumerAddrsToPrune(chainID, vscID, oldConsumerAddr) - } else { - // the validator had no key assigned on this consumer chain - oldConsumerKey := validator.TmConsPublicKey() - } - - // check whether the validator is valid, i.e., its power is positive - if currentPower := stakingKeeper.GetLastValidatorPower(providerAddr); currentPower > 0 { - // to enable multiple calls of AssignConsumerKey in the same block by the same validator - // the key assignment replacement should not be overwritten - if _, found := GetKeyAssignmentReplacement(chainID, providerConsAddr); !found { - // store old key and power for modifying the valset update in EndBlock - oldKeyAssignment := abci.ValidatorUpdate{PubKey: oldConsumerKey, Power: currentPower} - SetKeyAssignmentReplacement(chainID, providerConsAddr, oldKeyAssignment) - } } } else { // if the consumer chain is not registered, then remove the previous reverse mapping @@ -129,89 +112,24 @@ func (k Keeper) MakeConsumerGenesis(chainID string) (gen consumertypes.GenesisSt } ``` -On `EndBlock` while queueing `VSCPacket`s to send to registered consumer chains: +Note that key assignment works hand-in-hand with [epochs](https://github.com/cosmos/interchain-security/blob/main/docs/docs/adrs/adr-014-epochs.md). +For each consumer chain, we store the consumer validator set that is currently (i.e., in this epoch) validating the consumer chain. +Specifically, for each validator in the set we store among others, the public key that it is using on the consumer chain during the current (i.e., ongoing) epoch. +At the end of every epoch, if there were validator set changes on the provider, then for every consumer chain, we construct a `VSCPacket` +with all the validator updates and add it to the list of `PendingVSCPacket`s. We compute the validator updates needed by a consumer chain by +comparing the stored list of consumer validators with the current bonded validators on the provider, with something similar to this: ```golang -func QueueVSCPackets() { - valUpdateID := GetValidatorSetUpdateId() - // get the validator updates from the staking module - valUpdates := stakingKeeper.GetValidatorUpdates() - - IterateConsumerChains(func(chainID, clientID string) (stop bool) { - // apply the key assignment to the validator updates - valUpdates := ApplyKeyAssignmentToValUpdates(chainID, valUpdates) - // .. - }) - // ... -} - -func ApplyKeyAssignmentToValUpdates( - chainID string, - valUpdates []abci.ValidatorUpdate, -) (newUpdates []abci.ValidatorUpdate) { - for _, valUpdate := range valUpdates { - providerAddr := utils.TMCryptoPublicKeyToConsAddr(valUpdate.PubKey) - - // if a key assignment replacement is found, then - // remove the valupdate with the old consumer key - // and create two new valupdates - prevConsumerKey, _, found := GetKeyAssignmentReplacement(chainID, providerAddr) - if found { - // set the old consumer key's power to 0 - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: prevConsumerKey, - Power: 0, - }) - // set the new consumer key's power to the power in the update - newConsumerKey := GetValidatorConsumerPubKey(chainID, providerAddr) - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: newConsumerKey, - Power: valUpdate.Power, - }) - // delete key assignment replacement - DeleteKeyAssignmentReplacement(chainID, providerAddr) - } else { - // there is no key assignment replacement; - // check if the validator's key is assigned - consumerKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr) - if found { - // replace the update containing the provider key - // with an update containing the consumer key - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: consumerKey, - Power: valUpdate.Power, - }) - } else { - // keep the same update - newUpdates = append(newUpdates, valUpdate) - } - } - } - - // iterate over the remaining key assignment replacements - IterateKeyAssignmentReplacements(chainID, func( - pAddr sdk.ConsAddress, - prevCKey tmprotocrypto.PublicKey, - power int64, - ) (stop bool) { - // set the old consumer key's power to 0 - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: prevCKey, - Power: 0, - }) - // set the new consumer key's power to the power in key assignment replacement - newConsumerKey := GetValidatorConsumerPubKey(chainID, pAddr) - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: newConsumerKey, - Power: power, - }) - return false - }) - - // remove all the key assignment replacements - - return newUpdates -} +// get the valset that has been validating the consumer chain during this epoch +currentValidators := GetConsumerValSet(consumerChain) +// generate the validator updates needed to be sent through a `VSCPacket` by comparing the current validators +// in the epoch with the latest bonded validators +valUpdates := DiffValidators(currentValidators, stakingmodule.GetBondedValidators()) +// update the current validators set for the upcoming epoch to be the latest bonded validators instead +SetConsumerValSet(stakingmodule.GetBondedValidators()) ``` +where `DiffValidators` internally checks if the consumer public key for a validator has changed since the last +epoch and if so generates a validator update. This way, a validator can change its consumer public key for a consumer +chain an arbitrary amount of times and only the last set consumer public key would be taken into account. On receiving a `SlashPacket` from a consumer chain with id `chainID` for a infraction of a validator `data.Validator`: ```golang diff --git a/docs/docs/introduction/params.md b/docs/docs/introduction/params.md index 5a9e8462c7..69994f261c 100644 --- a/docs/docs/introduction/params.md +++ b/docs/docs/introduction/params.md @@ -149,3 +149,10 @@ This param would allow provider binaries to panic deterministically in the event `RetryDelayPeriod` exists on the consumer for **ICS versions >= v3.2.0** (introduced by the implementation of [ADR-008](../adrs/adr-008-throttle-retries.md)) and is the period at which the consumer retries to send a `SlashPacket` that was rejected by the provider. + +## Epoch Parameters + +### BlocksPerEpoch +`BlocksPerEpoch` exists on the provider for **ICS versions >= 3.3.0** (introduced by the implementation of [ADR-014](../adrs/adr-014-epochs.md)) +and corresponds to the number of blocks that constitute an epoch. This param is set to 600 by default and cannot exceed 1200. +Assuming we need 6 seconds per block, the default value corresponds to 1 hour and the maximum to 2 hours. \ No newline at end of file diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index f9bdf0a53f..4da89022e8 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -188,6 +188,9 @@ message Params { // The fee required to be paid to add a reward denom 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; } // SlashAcks contains cons addresses of consumer chain validators @@ -295,3 +298,14 @@ message ConsumerAddrsToPrune { uint64 vsc_id = 2; AddressList consumer_addrs = 3; } + +// ConsumerValidator is used to facilitate epoch-based transitions. It contains relevant info for +// a validator that is expected to validate on a consumer chain during an epoch. +message ConsumerValidator { + // validator's consensus address on the provider chain + bytes provider_cons_addr = 1; + // voting power the validator has during this epoch + int64 power = 2; + // public key the validator uses on the consumer chain during this epoch + tendermint.crypto.PublicKey consumer_public_key = 3; +} \ No newline at end of file diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 81bc3c8b76..c6e5424813 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -1307,6 +1307,12 @@ func (tr TestConfig) relayPacketsGorelayer( target ExecutionTarget, verbose bool, ) { + // Because `.app_state.provider.params.blocks_per_epoch` is set to 3 in the E2E tests, we wait 3 blocks + // before relaying the packets to guarantee that at least one epoch passes and hence any `VSCPacket`s get + // queued and are subsequently relayed. + tr.waitBlocks(action.ChainA, 3, 90*time.Second) + tr.waitBlocks(action.ChainB, 3, 90*time.Second) + pathName := tr.GetPathNameForGorelayer(action.ChainA, action.ChainB) // rly transact relay-packets [path-name] --channel [channel-id] @@ -1331,6 +1337,12 @@ func (tr TestConfig) relayPacketsHermes( target ExecutionTarget, verbose bool, ) { + // Because `.app_state.provider.params.blocks_per_epoch` is set to 3 in the E2E tests, we wait 3 blocks + // before relaying the packets to guarantee that at least one epoch passes and hence any `VSCPacket`s get + // queued and are subsequently relayed. + tr.waitBlocks(action.ChainA, 3, 90*time.Second) + tr.waitBlocks(action.ChainB, 3, 90*time.Second) + // hermes clear packets ibc0 transfer channel-13 cmd := target.ExecCommand("hermes", "clear", "packets", "--chain", string(tr.chainConfigs[action.ChainA].ChainId), @@ -1639,7 +1651,7 @@ func (tr TestConfig) invokeDowntimeSlash(action DowntimeSlashAction, target Exec // Bring validator down tr.setValidatorDowntime(action.Chain, action.Validator, true, target, verbose) // Wait appropriate amount of blocks for validator to be slashed - tr.waitBlocks(action.Chain, 10, 3*time.Minute) + tr.waitBlocks(action.Chain, 11, 3*time.Minute) // Bring validator back up tr.setValidatorDowntime(action.Chain, action.Validator, false, target, verbose) } diff --git a/tests/e2e/config.go b/tests/e2e/config.go index 307f61d62c..c9a71f8647 100644 --- a/tests/e2e/config.go +++ b/tests/e2e/config.go @@ -240,7 +240,8 @@ func SlashThrottleTestConfig() TestConfig { ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " + ".app_state.provider.params.slash_meter_replenish_fraction = \"0.10\" | " + - ".app_state.provider.params.slash_meter_replenish_period = \"20s\"", + ".app_state.provider.params.slash_meter_replenish_period = \"20s\" | " + + ".app_state.provider.params.blocks_per_epoch = 3", }, ChainID("consu"): { ChainId: ChainID("consu"), @@ -249,7 +250,7 @@ func SlashThrottleTestConfig() TestConfig { IpPrefix: "7.7.8", VotingWaitTime: 20, GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " + - ".app_state.slashing.params.signed_blocks_window = \"15\" | " + + ".app_state.slashing.params.signed_blocks_window = \"20\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " + @@ -288,7 +289,8 @@ func DefaultTestConfig() TestConfig { ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " + ".app_state.provider.params.slash_meter_replenish_fraction = \"1.0\" | " + // This disables slash packet throttling - ".app_state.provider.params.slash_meter_replenish_period = \"3s\"", + ".app_state.provider.params.slash_meter_replenish_period = \"3s\" | " + + ".app_state.provider.params.blocks_per_epoch = 3", }, ChainID("consu"): { ChainId: ChainID("consu"), @@ -297,7 +299,7 @@ func DefaultTestConfig() TestConfig { IpPrefix: "7.7.8", VotingWaitTime: 20, GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " + - ".app_state.slashing.params.signed_blocks_window = \"15\" | " + + ".app_state.slashing.params.signed_blocks_window = \"20\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"", @@ -317,7 +319,8 @@ func DemocracyTestConfig(allowReward bool) TestConfig { ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " + - ".app_state.transfer.params.send_enabled = false" + ".app_state.transfer.params.send_enabled = false | " + + ".app_state.provider.params.blocks_per_epoch = 3" if allowReward { // This allows the consumer chain to send rewards in the stake denom @@ -347,7 +350,8 @@ func DemocracyTestConfig(allowReward bool) TestConfig { ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " + - ".app_state.provider.params.slash_meter_replenish_fraction = \"1.0\"", // This disables slash packet throttling + ".app_state.provider.params.slash_meter_replenish_fraction = \"1.0\" | " + // This disables slash packet throttling + ".app_state.provider.params.blocks_per_epoch = 3", }, ChainID("democ"): { ChainId: ChainID("democ"), @@ -389,7 +393,8 @@ func MultiConsumerTestConfig() TestConfig { ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " + - ".app_state.provider.params.slash_meter_replenish_fraction = \"1.0\"", // This disables slash packet throttling + ".app_state.provider.params.slash_meter_replenish_fraction = \"1.0\" | " + // This disables slash packet throttling + ".app_state.provider.params.blocks_per_epoch = 3", }, ChainID("consu"): { ChainId: ChainID("consu"), @@ -398,7 +403,7 @@ func MultiConsumerTestConfig() TestConfig { IpPrefix: "7.7.8", VotingWaitTime: 20, GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " + - ".app_state.slashing.params.signed_blocks_window = \"10\" | " + + ".app_state.slashing.params.signed_blocks_window = \"20\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"", @@ -410,7 +415,7 @@ func MultiConsumerTestConfig() TestConfig { IpPrefix: "7.7.9", VotingWaitTime: 20, GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " + - ".app_state.slashing.params.signed_blocks_window = \"10\" | " + + ".app_state.slashing.params.signed_blocks_window = \"20\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"", @@ -448,7 +453,8 @@ func ChangeoverTestConfig() TestConfig { ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " + ".app_state.provider.params.slash_meter_replenish_fraction = \"1.0\" | " + // This disables slash packet throttling - ".app_state.provider.params.slash_meter_replenish_period = \"3s\"", + ".app_state.provider.params.slash_meter_replenish_period = \"3s\" | " + + ".app_state.provider.params.blocks_per_epoch = 3", }, ChainID("sover"): { ChainId: ChainID("sover"), @@ -458,7 +464,7 @@ func ChangeoverTestConfig() TestConfig { IpPrefix: "7.7.8", VotingWaitTime: 20, GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " + - ".app_state.slashing.params.signed_blocks_window = \"15\" | " + + ".app_state.slashing.params.signed_blocks_window = \"20\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " + @@ -548,7 +554,8 @@ func ConsumerMisbehaviourTestConfig() TestConfig { ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\" | " + ".app_state.provider.params.slash_meter_replenish_fraction = \"1.0\" | " + // This disables slash packet throttling - ".app_state.provider.params.slash_meter_replenish_period = \"3s\"", + ".app_state.provider.params.slash_meter_replenish_period = \"3s\" | " + + ".app_state.provider.params.blocks_per_epoch = 3", }, ChainID("consu"): { ChainId: ChainID("consu"), @@ -557,7 +564,7 @@ func ConsumerMisbehaviourTestConfig() TestConfig { IpPrefix: "7.7.8", VotingWaitTime: 20, GenesisChanges: ".app_state.gov.params.voting_period = \"20s\" | " + - ".app_state.slashing.params.signed_blocks_window = \"15\" | " + + ".app_state.slashing.params.signed_blocks_window = \"20\" | " + ".app_state.slashing.params.min_signed_per_window = \"0.500000000000000000\" | " + ".app_state.slashing.params.downtime_jail_duration = \"60s\" | " + ".app_state.slashing.params.slash_fraction_downtime = \"0.010000000000000000\"", diff --git a/tests/integration/common.go b/tests/integration/common.go index a4ff9e254a..18b657ae56 100644 --- a/tests/integration/common.go +++ b/tests/integration/common.go @@ -127,7 +127,7 @@ func delegateAndRedelegate(s *CCVTestSuite, delAddr sdk.AccAddress, srcValTokensAfter := s.getVal(s.providerCtx(), srcValAddr).GetBondedTokens() s.Require().Equal(srcValTokensAfter.Sub(srcValTokensBefore), amount) - s.providerChain.NextBlock() + s.nextEpoch() dstValTokensBefore := s.getVal(s.providerCtx(), dstValAddr).GetBondedTokens() @@ -625,3 +625,12 @@ func (s *CCVTestSuite) mustGetStakingValFromTmVal(tmVal tmtypes.Validator) (stak s.Require().True(found) return stakingVal } + +// nextEpoch moves `chain` forward by an epoch +func (s *CCVTestSuite) nextEpoch() { + blocksPerEpoch := s.providerApp.GetProviderKeeper().GetParams(s.providerCtx()).BlocksPerEpoch + + for i := int64(0); i < blocksPerEpoch; i++ { + s.providerChain.NextBlock() + } +} diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index 25cbcb3132..1ea5bfcd00 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -23,7 +23,7 @@ func (s *CCVTestSuite) TestRewardsDistribution() { bondAmt := sdk.NewInt(10000000) delAddr := s.providerChain.SenderAccount.GetAddress() delegate(s, delAddr, bondAmt) - s.providerChain.NextBlock() + s.nextEpoch() // register a consumer reward denom params := s.consumerApp.GetConsumerKeeper().GetConsumerParams(s.consumerCtx()) @@ -124,7 +124,7 @@ func (s *CCVTestSuite) TestSendRewardsRetries() { bondAmt := sdk.NewInt(10000000) delAddr := s.providerChain.SenderAccount.GetAddress() delegate(s, delAddr, bondAmt) - s.providerChain.NextBlock() + s.nextEpoch() // Register denom on consumer chain params := s.consumerApp.GetConsumerKeeper().GetConsumerParams(s.consumerCtx()) @@ -253,7 +253,7 @@ func (s *CCVTestSuite) TestEndBlockRD() { bondAmt := sdk.NewInt(10000000) delAddr := s.providerChain.SenderAccount.GetAddress() delegate(s, delAddr, bondAmt) - s.providerChain.NextBlock() + s.nextEpoch() if tc.denomRegistered { params := s.consumerApp.GetConsumerKeeper().GetConsumerParams(s.consumerCtx()) diff --git a/tests/integration/expired_client.go b/tests/integration/expired_client.go index 1981e85828..cfba19803e 100644 --- a/tests/integration/expired_client.go +++ b/tests/integration/expired_client.go @@ -33,7 +33,7 @@ func (s *CCVTestSuite) TestVSCPacketSendExpiredClient() { delegate(s, delAddr, bondAmt) // try to send CCV packet to consumer - s.providerChain.NextBlock() + s.nextEpoch() // check that the packet was added to the list of pending VSC packets packets := providerKeeper.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) @@ -41,7 +41,7 @@ func (s *CCVTestSuite) TestVSCPacketSendExpiredClient() { s.Require().Equal(1, len(packets), "unexpected number of pending VSC packets") // try again to send CCV packet to consumer - s.providerChain.NextBlock() + s.nextEpoch() // check that the packet is still in the list of pending VSC packets packets = providerKeeper.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) @@ -52,7 +52,7 @@ func (s *CCVTestSuite) TestVSCPacketSendExpiredClient() { delegate(s, delAddr, bondAmt) // try again to send CCV packets to consumer - s.providerChain.NextBlock() + s.nextEpoch() // check that the packets are still in the list of pending VSC packets packets = providerKeeper.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) @@ -62,8 +62,8 @@ func (s *CCVTestSuite) TestVSCPacketSendExpiredClient() { // upgrade expired client to the consumer upgradeExpiredClient(s, Consumer) - // go to next block - s.providerChain.NextBlock() + // go to next epoch + s.nextEpoch() // check that the packets are not in the list of pending VSC packets packets = providerKeeper.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) @@ -73,7 +73,7 @@ func (s *CCVTestSuite) TestVSCPacketSendExpiredClient() { // - bond more tokens on provider to change validator powers delegate(s, delAddr, bondAmt) // - send CCV packet to consumer - s.providerChain.NextBlock() + s.nextEpoch() // - relay all VSC packet from provider to consumer relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 3) // - increment time so that the unbonding period ends on the consumer @@ -102,7 +102,7 @@ func (s *CCVTestSuite) TestConsumerPacketSendExpiredClient() { delegate(s, delAddr, bondAmt) // send CCV packets to consumer - s.providerChain.NextBlock() + s.nextEpoch() // check that the packets are not in the list of pending VSC packets providerPackets := providerKeeper.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) @@ -172,7 +172,7 @@ func (s *CCVTestSuite) TestConsumerPacketSendExpiredClient() { // - bond more tokens on provider to change validator powers delegate(s, delAddr, bondAmt) // - send CCV packet to consumer - s.providerChain.NextBlock() + s.nextEpoch() // - relay 1 VSC packet from provider to consumer relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 1) // - increment time so that the unbonding period ends on the provider diff --git a/tests/integration/key_assignment.go b/tests/integration/key_assignment.go index 20e746ae63..ab6cb63146 100644 --- a/tests/integration/key_assignment.go +++ b/tests/integration/key_assignment.go @@ -30,7 +30,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { } // check that a VSCPacket is queued - s.providerChain.NextBlock() + s.nextEpoch() pendingPackets := pk.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) s.Require().Len(pendingPackets, 1) @@ -51,7 +51,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } - s.providerChain.NextBlock() + s.nextEpoch() return nil }, false, 2, @@ -73,7 +73,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { delAddr := s.providerChain.SenderAccount.GetAddress() delegate(s, delAddr, bondAmt) - s.providerChain.NextBlock() + s.nextEpoch() return nil }, false, 2, @@ -95,7 +95,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } - s.providerChain.NextBlock() + s.nextEpoch() return nil }, true, 2, @@ -118,7 +118,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } - s.providerChain.NextBlock() + s.nextEpoch() return nil }, false, 2, @@ -134,14 +134,14 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } - s.providerChain.NextBlock() + s.nextEpoch() // same key assignment err = pk.AssignConsumerKey(s.providerCtx(), s.consumerChain.ChainID, validator, consumerKey) if err != nil { return err } - s.providerChain.NextBlock() + s.nextEpoch() return nil }, true, 2, @@ -157,7 +157,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } - s.providerChain.NextBlock() + s.nextEpoch() // same key assignment validator, consumerKey = generateNewConsumerKey(s, 0) @@ -165,7 +165,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { if err != nil { return err } - s.providerChain.NextBlock() + s.nextEpoch() return nil }, false, 3, diff --git a/tests/integration/setup.go b/tests/integration/setup.go index e401324c82..03c63d5502 100644 --- a/tests/integration/setup.go +++ b/tests/integration/setup.go @@ -129,6 +129,12 @@ func (suite *CCVTestSuite) SetupTest() { suite.registerPacketSniffer(suite.providerChain) providerKeeper := suite.providerApp.GetProviderKeeper() + // set `BlocksPerEpoch` to 10: a reasonable small value greater than 1 that prevents waiting for too + // many blocks and slowing down the integration tests + params := providerKeeper.GetParams(suite.providerCtx()) + params.BlocksPerEpoch = 10 + providerKeeper.SetParams(suite.providerCtx(), params) + // re-assign all validator keys for the first consumer chain providerKeeper.SetPendingConsumerAdditionProp(suite.providerCtx(), &types.ConsumerAdditionProposal{ ChainId: icstestingutils.FirstConsumerChainID, diff --git a/tests/integration/slashing.go b/tests/integration/slashing.go index 2339538292..e7f585f756 100644 --- a/tests/integration/slashing.go +++ b/tests/integration/slashing.go @@ -107,8 +107,10 @@ func (s *CCVTestSuite) TestRelayAndApplyDowntimePacket() { s.Require().True(found) } + s.nextEpoch() + // Confirm the valset update Id was incremented twice on provider, - // since two endblockers have passed. + // since an epoch has passed. s.Require().Equal(valsetUpdateIdN+2, providerKeeper.GetValidatorSetUpdateId(s.providerCtx())) diff --git a/tests/integration/soft_opt_out.go b/tests/integration/soft_opt_out.go index a5ee566a4b..a9508118bd 100644 --- a/tests/integration/soft_opt_out.go +++ b/tests/integration/soft_opt_out.go @@ -73,7 +73,7 @@ func (suite *CCVTestSuite) TestSoftOptOut() { bondAmt := sdk.NewInt(100).Mul(sdk.DefaultPowerReduction) delegateByIdx(suite, delAddr, bondAmt, valIdx) - suite.providerChain.NextBlock() + suite.nextEpoch() // Relay 1 VSC packet from provider to consumer relayAllCommittedPackets(suite, suite.providerChain, suite.path, ccv.ProviderPortID, suite.path.EndpointB.ChannelID, 1) @@ -112,7 +112,7 @@ func (suite *CCVTestSuite) TestSoftOptOut() { bondAmt := sdk.NewInt(100).Mul(sdk.DefaultPowerReduction) delegateByIdx(suite, delAddr, bondAmt, valIdx) - suite.providerChain.NextBlock() + suite.nextEpoch() // Relay 1 VSC packet from provider to consumer relayAllCommittedPackets(suite, suite.providerChain, suite.path, ccv.ProviderPortID, suite.path.EndpointB.ChannelID, 1) @@ -149,6 +149,8 @@ func (suite *CCVTestSuite) TestSoftOptOut() { validatorPowers := []int64{1000, 500, 50, 10} suite.setupValidatorPowers(validatorPowers) + suite.nextEpoch() + // Relay 1 VSC packet from provider to consumer relayAllCommittedPackets(suite, suite.providerChain, suite.path, ccv.ProviderPortID, suite.path.EndpointB.ChannelID, 1) diff --git a/tests/integration/unbonding.go b/tests/integration/unbonding.go index 00f48871c2..7f87516444 100644 --- a/tests/integration/unbonding.go +++ b/tests/integration/unbonding.go @@ -231,8 +231,7 @@ func (s *CCVTestSuite) TestUndelegationDuringInit() { // update init timeout timestamp tc.updateInitTimeoutTimestamp(&providerKeeper, providerUnbondingPeriod) - // call NextBlock on the provider (which increments the height) - s.providerChain.NextBlock() + s.nextEpoch() // check that the VSC packet is stored in state as pending pendingVSCs := providerKeeper.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) @@ -241,8 +240,7 @@ func (s *CCVTestSuite) TestUndelegationDuringInit() { // delegate again to create another VSC packet delegate(s, delAddr, bondAmt) - // call NextBlock on the provider (which increments the height) - s.providerChain.NextBlock() + s.nextEpoch() // check that the VSC packet is stored in state as pending pendingVSCs = providerKeeper.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) @@ -266,6 +264,7 @@ func (s *CCVTestSuite) TestUndelegationDuringInit() { // complete CCV channel setup s.SetupCCVChannel(s.path) + s.nextEpoch() // relay VSC packets from provider to consumer relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 2) @@ -429,8 +428,8 @@ func (s *CCVTestSuite) TestRedelegationProviderFirst() { // Check that CCV unbonding op was created from AfterUnbondingInitiated hook checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, true) - // Call NextBlock on the provider (which increments the height) - s.providerChain.NextBlock() + // move forward by an epoch to be able to relay VSC packets + s.nextEpoch() // Relay 2 VSC packets from provider to consumer (original delegation, and redelegation) relayAllCommittedPackets(s, s.providerChain, s.path, diff --git a/tests/integration/valset_update.go b/tests/integration/valset_update.go index dedcce2b86..eb0560a35e 100644 --- a/tests/integration/valset_update.go +++ b/tests/integration/valset_update.go @@ -23,8 +23,8 @@ func (s *CCVTestSuite) TestPacketRoundtrip() { delAddr := s.providerChain.SenderAccount.GetAddress() delegate(s, delAddr, bondAmt) - // Send CCV packet to consumer - s.providerChain.NextBlock() + // Send CCV packet to consumer at the end of the epoch + s.nextEpoch() // Relay 1 VSC packet from provider to consumer relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 1) diff --git a/tests/mbt/driver/mbt_test.go b/tests/mbt/driver/mbt_test.go index a55d870dda..183839dc9a 100644 --- a/tests/mbt/driver/mbt_test.go +++ b/tests/mbt/driver/mbt_test.go @@ -184,6 +184,12 @@ func RunItfTrace(t *testing.T, path string) { driver.setupProvider(modelParams, valSet, signers, nodes, valNames) + // set `BlocksPerEpoch` to 10: a reasonable small value greater than 1 that prevents waiting for too + // many blocks and slowing down the tests + providerParams := driver.providerKeeper().GetParams(driver.providerCtx()) + providerParams.BlocksPerEpoch = 10 + driver.providerKeeper().SetParams(driver.providerCtx(), providerParams) + // remember the time offsets to be able to compare times to the model // this is necessary because the system needs to do many steps to initialize the chains, // which is abstracted away in the model @@ -233,17 +239,23 @@ func RunItfTrace(t *testing.T, path string) { stats.numStartedChains += len(consumersToStart) stats.numStops += len(consumersToStop) - // we need 2 blocks, because for a packet sent at height H, the receiving chain + // we need at least 2 blocks, because for a packet sent at height H, the receiving chain // needs a header of height H+1 to accept the packet - // so we do one time advancement with a very small increment, + // so, we do `blocksPerEpoch` time advancements with a very small increment, // and then increment the rest of the time runningConsumersBefore := driver.runningConsumers() - driver.endAndBeginBlock("provider", 1*time.Nanosecond) + + // going through `blocksPerEpoch` blocks to take into account an epoch + blocksPerEpoch := driver.providerKeeper().GetBlocksPerEpoch(driver.providerCtx()) + for i := int64(0); i < blocksPerEpoch; i = i + 1 { + driver.endAndBeginBlock("provider", 1*time.Nanosecond) + } for _, consumer := range driver.runningConsumers() { UpdateProviderClientOnConsumer(t, driver, consumer.ChainId) } - driver.endAndBeginBlock("provider", time.Duration(timeAdvancement)*time.Second-1*time.Nanosecond) + driver.endAndBeginBlock("provider", time.Duration(timeAdvancement)*time.Second-time.Nanosecond*time.Duration(blocksPerEpoch)) + runningConsumersAfter := driver.runningConsumers() // the consumers that were running before but not after must have timed out diff --git a/tests/mbt/driver/setup.go b/tests/mbt/driver/setup.go index 83fa6e0669..69b385cb77 100644 --- a/tests/mbt/driver/setup.go +++ b/tests/mbt/driver/setup.go @@ -356,6 +356,26 @@ func (s *Driver) ConfigureNewPath(consumerChain, providerChain *ibctesting.TestC NewChain: consumerGenesis.NewChain, } + var stakingValidators []stakingtypes.Validator + + // set up the current consumer validators by utilizing the initial validator set + for _, val := range consumerGenesisForProvider.Provider.InitialValSet { + pubKey := val.PubKey + consAddr, err := ccvtypes.TMCryptoPublicKeyToConsAddr(pubKey) + if err != nil { + continue + } + + v, found := s.providerStakingKeeper().GetValidatorByConsAddr(s.providerCtx(), consAddr) + if !found { + continue + } + stakingValidators = append(stakingValidators, v) + } + + nextValidators := s.providerKeeper().ComputeNextEpochConsumerValSet(s.providerCtx(), string(consumerChainId), stakingValidators) + s.providerKeeper().SetConsumerValSet(s.providerCtx(), string(consumerChainId), nextValidators) + err = s.providerKeeper().SetConsumerGenesis( providerChain.GetContext(), string(consumerChainId), diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index dc901712ff..ff3df99763 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -252,7 +252,6 @@ func TestProviderStateIsCleanedAfterConsumerChainIsStopped(t *testing.T, ctx sdk // test key assignment state is cleaned require.Empty(t, providerKeeper.GetAllValidatorConsumerPubKeys(ctx, &expectedChainID)) require.Empty(t, providerKeeper.GetAllValidatorsByConsumerAddr(ctx, &expectedChainID)) - require.Empty(t, providerKeeper.GetAllKeyAssignmentReplacements(ctx, expectedChainID)) require.Empty(t, providerKeeper.GetAllConsumerAddrsToPrune(ctx, expectedChainID)) } diff --git a/x/ccv/consumer/types/keys.go b/x/ccv/consumer/types/keys.go index 0292ca84c5..20163f5ed9 100644 --- a/x/ccv/consumer/types/keys.go +++ b/x/ccv/consumer/types/keys.go @@ -51,7 +51,7 @@ const ( // received over CCV channel but not yet flushed over ABCI PendingChangesByteKey - // NOTE: This prefix is depreciated, but left in place to avoid consumer state migrations + // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations // [DEPRECATED] PendingDataPacketsByteKey @@ -61,7 +61,7 @@ const ( // InitialValSetByteKey is the byte to store the initial validator set for a consumer InitialValSetByteKey - // NOTE: This prefix is depreciated, but left in place to avoid consumer state migrations + // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations // [DEPRECATED] LastStandaloneHeightByteKey diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index dfe0a73895..e3273d1fb5 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -23,7 +23,6 @@ func TestQueryAllPairsValConAddrByConsumerChainID(t *testing.T) { defer ctrl.Finish() pk.SetValidatorConsumerPubKey(ctx, chainID, providerAddr, consumerKey) - pk.SetKeyAssignmentReplacement(ctx, chainID, providerAddr, consumerKey, 100) consumerPubKey, found := pk.GetValidatorConsumerPubKey(ctx, chainID, providerAddr) require.True(t, found, "consumer pubkey not found") diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index c54d922f0f..89dbcfda4e 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -8,7 +8,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" @@ -184,99 +183,6 @@ func (k Keeper) DeleteValidatorByConsumerAddr(ctx sdk.Context, chainID string, c store.Delete(types.ValidatorsByConsumerAddrKey(chainID, consumerAddr)) } -// GetKeyAssignmentReplacement returns the previous assigned consumer key and the current power -// for a provider validator for which a key assignment was received in this block. Both are -// needed to update the validator's power on the consumer chain at the end of the current block. -func (k Keeper) GetKeyAssignmentReplacement( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) (prevCKey tmprotocrypto.PublicKey, power int64, found bool) { - var pubKeyAndPower abci.ValidatorUpdate - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.KeyAssignmentReplacementsKey(chainID, providerAddr)) - if bz == nil { - return pubKeyAndPower.PubKey, pubKeyAndPower.Power, false - } - - err := pubKeyAndPower.Unmarshal(bz) - if err != nil { - // An error here would indicate something is very wrong, - // the public key and power are assumed to be correctly serialized in SetKeyAssignmentReplacement. - panic(fmt.Sprintf("failed to unmarshal public key and power: %v", err)) - } - return pubKeyAndPower.PubKey, pubKeyAndPower.Power, true -} - -// SetKeyAssignmentReplacement sets the previous assigned consumer key and the current power -// for a provider validator for which a key assignment was received in this block. Both are -// needed to update the validator's power on the consumer chain at the end of the current block. -func (k Keeper) SetKeyAssignmentReplacement( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, - prevCKey tmprotocrypto.PublicKey, - power int64, -) { - store := ctx.KVStore(k.storeKey) - pubKeyAndPower := abci.ValidatorUpdate{PubKey: prevCKey, Power: power} - bz, err := pubKeyAndPower.Marshal() - if err != nil { - // An error here would indicate something is very wrong, - // prevCKey is obtained from GetValidatorConsumerPubKey (called from AssignConsumerKey), - // and power is obtained from GetLastValidatorPower (called from AssignConsumerKey). - // Both of which are assumed to return valid values. - panic(fmt.Sprintf("failed to marshal public key and power: %v", err)) - } - store.Set(types.KeyAssignmentReplacementsKey(chainID, providerAddr), bz) -} - -// GetAllKeyAssignmentReplacements gets all pairs of previous assigned consumer keys -// and current powers for all provider validator for which key assignments were received in this block. -// -// Note that the pairs are stored under keys with the following format: -// KeyAssignmentReplacementsBytePrefix | len(chainID) | chainID | providerAddress -// Thus, the iteration is in ascending order of providerAddresses. -func (k Keeper) GetAllKeyAssignmentReplacements(ctx sdk.Context, chainID string) (replacements []types.KeyAssignmentReplacement) { - store := ctx.KVStore(k.storeKey) - iteratorPrefix := types.ChainIdWithLenKey(types.KeyAssignmentReplacementsBytePrefix, chainID) - iterator := sdk.KVStorePrefixIterator(store, iteratorPrefix) - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - // TODO: store chainID and provider cons address in value bytes, marshaled as protobuf type - _, providerAddrTmp, err := types.ParseChainIdAndConsAddrKey(types.KeyAssignmentReplacementsBytePrefix, iterator.Key()) - if err != nil { - // An error here would indicate something is very wrong, - // store keys are assumed to be correctly serialized in SetKeyAssignmentReplacement. - panic(err) - } - providerAddr := types.NewProviderConsAddress(providerAddrTmp) - var pubKeyAndPower abci.ValidatorUpdate - err = pubKeyAndPower.Unmarshal(iterator.Value()) - if err != nil { - // An error here would indicate something is very wrong, - // the public key and power are assumed to be correctly serialized in SetKeyAssignmentReplacement. - panic(fmt.Sprintf("failed to unmarshal public key and power: %v", err)) - } - - replacements = append(replacements, types.KeyAssignmentReplacement{ - ProviderAddr: providerAddr.ToSdkConsAddr(), - PrevCKey: &pubKeyAndPower.PubKey, - Power: pubKeyAndPower.Power, - }) - } - - return replacements -} - -// DeleteKeyAssignmentReplacement deletes the previous assigned consumer key and the current power -// for a provider validator for which a key assignment was received in this block. Both are -// needed to update the validator's power on the consumer chain at the end of the current block. -func (k Keeper) DeleteKeyAssignmentReplacement(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.KeyAssignmentReplacementsKey(chainID, providerAddr)) -} - // AppendConsumerAddrsToPrune appends a consumer validator address to the list of consumer addresses // that can be pruned once the VSCMaturedPacket with vscID is received. // @@ -425,20 +331,20 @@ func (k Keeper) AssignConsumerKey( ) } - // check whether the consumer chain is already registered, - // i.e., a client to the consumer was already created - if _, consumerRegistered := k.GetConsumerClientId(ctx, chainID); consumerRegistered { - // get the previous key assigned for this validator on this consumer chain - oldConsumerKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr) - if found { - // mark this old consumer key as prunable once the VSCMaturedPacket + // get the previous key assigned for this validator on this consumer chain + if oldConsumerKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr); found { + oldConsumerAddrTmp, err := ccvtypes.TMCryptoPublicKeyToConsAddr(oldConsumerKey) + if err != nil { + return err + } + oldConsumerAddr := types.NewConsumerConsAddress(oldConsumerAddrTmp) + + // check whether the consumer chain is already registered, + // i.e., a client to the consumer was already created + if _, consumerRegistered := k.GetConsumerClientId(ctx, chainID); consumerRegistered { + // mark the old consumer address as prunable once the VSCMaturedPacket // for the current VSC ID is received; // note: this state is removed on receiving the VSCMaturedPacket - oldConsumerAddrTmp, err := ccvtypes.TMCryptoPublicKeyToConsAddr(oldConsumerKey) - if err != nil { - return err - } - oldConsumerAddr := types.NewConsumerConsAddress(oldConsumerAddrTmp) k.AppendConsumerAddrsToPrune( ctx, chainID, @@ -446,42 +352,8 @@ func (k Keeper) AssignConsumerKey( oldConsumerAddr, ) } else { - // the validator had no key assigned on this consumer chain - providerKey, err := validator.TmConsPublicKey() - if err != nil { - return err - } - oldConsumerKey = providerKey - } - - // check whether the validator is valid, i.e., its power is positive - power := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) - if 0 < power { - // to enable multiple calls of AssignConsumerKey in the same block by the same validator - - // the key assignment replacement should not be overwritten - if _, _, found := k.GetKeyAssignmentReplacement(ctx, chainID, providerAddr); !found { - // store old key and current power for modifying the valset update in EndBlock; - // note: this state is deleted at the end of the block - k.SetKeyAssignmentReplacement( - ctx, - chainID, - providerAddr, - oldConsumerKey, - power, - ) - } - } - } else { - // if the consumer chain is not registered, then remove the mapping - // from the old consumer address to the provider address (if any) - // get the previous key assigned for this validator on this consumer chain - if oldConsumerKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr); found { - oldConsumerAddrTmp, err := ccvtypes.TMCryptoPublicKeyToConsAddr(oldConsumerKey) - if err != nil { - return err - } - oldConsumerAddr := types.NewConsumerConsAddress(oldConsumerAddrTmp) + // if the consumer chain is not registered, then remove the mapping + // from the old consumer address to the provider address k.DeleteValidatorByConsumerAddr(ctx, chainID, oldConsumerAddr) } } @@ -499,88 +371,6 @@ func (k Keeper) AssignConsumerKey( return nil } -// MustApplyKeyAssignmentToValUpdates applies the key assignment to the validator updates -// received from the staking module. -// The method panics if the key-assignment state is corrupted. -func (k Keeper) MustApplyKeyAssignmentToValUpdates( - ctx sdk.Context, - chainID string, - valUpdates []abci.ValidatorUpdate, -) (newUpdates []abci.ValidatorUpdate) { - for _, valUpdate := range valUpdates { - providerAddrTmp, err := ccvtypes.TMCryptoPublicKeyToConsAddr(valUpdate.PubKey) - if err != nil { - panic(fmt.Errorf("cannot get provider address from pub key: %s", err.Error())) - } - providerAddr := types.NewProviderConsAddress(providerAddrTmp) - - // If a key assignment replacement is found, we remove the valupdate with the old consumer key, - // create two new valupdates, - // - setting the old consumer key's power to 0 - // - and setting the new consumer key's power to the power in the update - prevConsumerKey, _, found := k.GetKeyAssignmentReplacement(ctx, chainID, providerAddr) - if found { - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: prevConsumerKey, - Power: 0, - }) - - newConsumerKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr) - if !found { - // This should never happen as for every KeyAssignmentReplacement there should - // be a ValidatorConsumerPubKey that was stored when AssignConsumerKey() was called. - panic(fmt.Errorf("consumer key not found for provider addr %s stored in KeyAssignmentReplacement", providerAddr)) - } - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: newConsumerKey, - Power: valUpdate.Power, - }) - k.DeleteKeyAssignmentReplacement(ctx, chainID, providerAddr) - } else { - // If a key assignment replacement is not found, we check if the validator's key is assigned. - // If it is, we replace the update containing the provider key with an update containing - // the consumer key. - // Note that this will always be the branch taken when creating the genesis state - // of a newly registered consumer chain. - consumerKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr) - if found { - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: consumerKey, - Power: valUpdate.Power, - }) - } else { - // keep the same update - newUpdates = append(newUpdates, valUpdate) - } - } - } - - // For any key assignment replacements that did not have a corresponding validator update already, - // set the old consumer key's power to 0 and the new consumer key's power to the - // power in the pending key assignment. - for _, replacement := range k.GetAllKeyAssignmentReplacements(ctx, chainID) { - providerAddr := types.NewProviderConsAddress(replacement.ProviderAddr) - k.DeleteKeyAssignmentReplacement(ctx, chainID, providerAddr) - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: *replacement.PrevCKey, - Power: 0, - }) - - newConsumerKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr) - if !found { - // This should never happen as for every KeyAssignmentReplacement there should - // be a ValidatorConsumerPubKey that was stored when AssignConsumerKey() was called. - panic(fmt.Errorf("consumer key not found for provider addr %s stored in KeyAssignmentReplacement", replacement.ProviderAddr)) - } - newUpdates = append(newUpdates, abci.ValidatorUpdate{ - PubKey: newConsumerKey, - Power: replacement.Power, - }) - } - - return newUpdates -} - // GetProviderAddrFromConsumerAddr returns the consensus address of a validator with // consAddr set as the consensus address on a consumer chain func (k Keeper) GetProviderAddrFromConsumerAddr( @@ -627,12 +417,6 @@ func (k Keeper) DeleteKeyAssignments(ctx sdk.Context, chainID string) { k.DeleteValidatorByConsumerAddr(ctx, chainID, consumerAddr) } - // delete KeyAssignmentReplacements - for _, keyAssignmentReplacement := range k.GetAllKeyAssignmentReplacements(ctx, chainID) { - providerAddr := types.NewProviderConsAddress(keyAssignmentReplacement.ProviderAddr) - k.DeleteKeyAssignmentReplacement(ctx, chainID, providerAddr) - } - // delete ValidatorConsumerPubKey for _, consumerAddrsToPrune := range k.GetAllConsumerAddrsToPrune(ctx, chainID) { k.DeleteConsumerAddrsToPrune(ctx, chainID, consumerAddrsToPrune.VscId) diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 4fab08c981..7cb222a3be 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "bytes" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "math/rand" "sort" "testing" @@ -184,67 +185,6 @@ func TestGetAllValidatorsByConsumerAddr(t *testing.T) { require.Len(t, result, len(testAssignments)) } -func TestKeyAssignmentReplacementCRUD(t *testing.T) { - chainID := consumer - providerAddr := types.NewProviderConsAddress([]byte("providerAddr")) - expCPubKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() - var expPower int64 = 100 - - keeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - keeper.SetKeyAssignmentReplacement(ctx, chainID, providerAddr, expCPubKey, expPower) - - cPubKey, power, found := keeper.GetKeyAssignmentReplacement(ctx, chainID, providerAddr) - require.True(t, found, "key assignment replacement not found") - require.Equal(t, expCPubKey, cPubKey, "previous consumer key not matching") - require.Equal(t, expPower, power, "power not matching") - - keeper.DeleteKeyAssignmentReplacement(ctx, chainID, providerAddr) - _, _, found = keeper.GetKeyAssignmentReplacement(ctx, chainID, providerAddr) - require.False(t, found, "key assignment replacement found") -} - -func TestGetAllKeyAssignmentReplacements(t *testing.T) { - pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - chainID := "consumer-1" - - seed := time.Now().UnixNano() - rng := rand.New(rand.NewSource(seed)) - - numAssignments := 10 - testAssignments := []types.KeyAssignmentReplacement{} - for i := 0; i < numAssignments; i++ { - consumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(i).TMProtoCryptoPublicKey() - providerAddr := cryptotestutil.NewCryptoIdentityFromIntSeed(numAssignments + i).ProviderConsAddress() - testAssignments = append(testAssignments, - types.KeyAssignmentReplacement{ - ProviderAddr: providerAddr.ToSdkConsAddr(), - PrevCKey: &consumerKey, - Power: rng.Int63(), - }, - ) - } - expectedGetAllOrder := testAssignments - // sorting by KeyAssignmentReplacement.ProviderAddr - sort.Slice(expectedGetAllOrder, func(i, j int) bool { - return bytes.Compare(expectedGetAllOrder[i].ProviderAddr, expectedGetAllOrder[j].ProviderAddr) == -1 - }) - - firstTestAssignmentProviderAddr := types.NewProviderConsAddress(testAssignments[0].ProviderAddr) - pk.SetKeyAssignmentReplacement(ctx, "consumer-2", firstTestAssignmentProviderAddr, *testAssignments[0].PrevCKey, testAssignments[0].Power) - for _, assignment := range testAssignments { - providerAddr := types.NewProviderConsAddress(assignment.ProviderAddr) - pk.SetKeyAssignmentReplacement(ctx, chainID, providerAddr, *assignment.PrevCKey, assignment.Power) - } - - result := pk.GetAllKeyAssignmentReplacements(ctx, chainID) - require.Len(t, result, len(testAssignments)) - require.Equal(t, expectedGetAllOrder, result) -} - func TestConsumerAddrsToPruneCRUD(t *testing.T) { chainID := consumer consumerAddr := types.NewConsumerConsAddress([]byte("consumerAddr1")) @@ -420,9 +360,6 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, consumerIdentities[0].SDKValConsAddress(), ).Return(stakingtypes.Validator{}, false), - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower( - ctx, providerIdentities[0].SDKValOpAddress(), - ).Return(int64(0)), ) }, doActions: func(ctx sdk.Context, k providerkeeper.Keeper) { @@ -445,15 +382,9 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, consumerIdentities[0].SDKValConsAddress(), ).Return(stakingtypes.Validator{}, false), - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower( - ctx, providerIdentities[0].SDKValOpAddress(), - ).Return(int64(0)), mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, consumerIdentities[1].SDKValConsAddress(), ).Return(stakingtypes.Validator{}, false), - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower( - ctx, providerIdentities[0].SDKValOpAddress(), - ).Return(int64(0)), ) }, doActions: func(ctx sdk.Context, k providerkeeper.Keeper) { @@ -481,9 +412,6 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, consumerIdentities[0].SDKValConsAddress(), ).Return(stakingtypes.Validator{}, false), - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower( - ctx, providerIdentities[0].SDKValOpAddress(), - ).Return(int64(0)), mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, consumerIdentities[0].SDKValConsAddress(), ).Return(stakingtypes.Validator{}, false), @@ -692,7 +620,6 @@ func (vs *ValSet) apply(updates []abci.ValidatorUpdate) { // note: an insertion index should always be found for _, u := range updates { for i, id := range vs.identities { // n2 looping but n is tiny - // cons := sdk.ConsAddress(utils.GetChangePubKeyAddress(u)) cons, _ := ccvtypes.TMCryptoPublicKeyToConsAddr(u.PubKey) if id.SDKValConsAddress().Equals(cons) { vs.power[i] = u.Power @@ -828,7 +755,21 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // and increment the provider vscid. applyUpdatesAndIncrementVSCID := func(updates []abci.ValidatorUpdate) { providerValset.apply(updates) - updates = k.MustApplyKeyAssignmentToValUpdates(ctx, CHAINID, updates) + + var bondedValidators []stakingtypes.Validator + for _, v := range providerValset.identities { + pkAny, _ := codectypes.NewAnyWithValue(v.ConsensusSDKPubKey()) + + bondedValidators = append(bondedValidators, stakingtypes.Validator{ + OperatorAddress: v.SDKValOpAddress().String(), + ConsensusPubkey: pkAny, + }) + } + + nextValidators := k.ComputeNextEpochConsumerValSet(ctx, CHAINID, bondedValidators) + updates = providerkeeper.DiffValidators(k.GetConsumerValSet(ctx, CHAINID), nextValidators) + k.SetConsumerValSet(ctx, CHAINID, nextValidators) + consumerValset.apply(updates) // Simulate the VSCID update in EndBlock k.IncrementValidatorSetUpdateId(ctx) @@ -844,10 +785,13 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // The consumer chain has not yet been registered // Apply some randomly generated key assignments - applyAssignments(getAssignments()) + assignments := getAssignments() + applyAssignments(assignments) // And generate a random provider valset which, in the real system, will // be put into the consumer genesis. - applyUpdatesAndIncrementVSCID(getStakingUpdates()) + stakingUpdates := getStakingUpdates() + + applyUpdatesAndIncrementVSCID(stakingUpdates) // Register the consumer chain k.SetConsumerClientId(ctx, CHAINID, "") @@ -861,9 +805,12 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // and a random set of validator power updates for block := 0; block < NUM_BLOCKS_PER_EXECUTION; block++ { + stakingUpdates = getStakingUpdates() + assignments = getAssignments() + // Generate and apply assignments and power updates - applyAssignments(getAssignments()) - applyUpdatesAndIncrementVSCID(getStakingUpdates()) + applyAssignments(assignments) + applyUpdatesAndIncrementVSCID(stakingUpdates) // Randomly fast forward the greatest pruned VSCID. This simulates // delivery of maturity packets from the consumer chain. diff --git a/x/ccv/provider/keeper/params.go b/x/ccv/provider/keeper/params.go index 209d0f0ddb..f74baf656e 100644 --- a/x/ccv/provider/keeper/params.go +++ b/x/ccv/provider/keeper/params.go @@ -78,6 +78,13 @@ func (k Keeper) GetConsumerRewardDenomRegistrationFee(ctx sdk.Context) sdk.Coin return c } +// GetBlocksPerEpoch returns the number of blocks that constitute an epoch +func (k Keeper) GetBlocksPerEpoch(ctx sdk.Context) int64 { + var b int64 + k.paramSpace.Get(ctx, types.KeyBlocksPerEpoch, &b) + return b +} + // GetParams returns the paramset for the provider module func (k Keeper) GetParams(ctx sdk.Context) types.Params { return types.NewParams( @@ -89,6 +96,7 @@ func (k Keeper) GetParams(ctx sdk.Context) types.Params { k.GetSlashMeterReplenishPeriod(ctx), k.GetSlashMeterReplenishFraction(ctx), k.GetConsumerRewardDenomRegistrationFee(ctx), + k.GetBlocksPerEpoch(ctx), ) } diff --git a/x/ccv/provider/keeper/params_test.go b/x/ccv/provider/keeper/params_test.go index a941523e87..88175431c0 100644 --- a/x/ccv/provider/keeper/params_test.go +++ b/x/ccv/provider/keeper/params_test.go @@ -48,6 +48,7 @@ func TestParams(t *testing.T) { Denom: "stake", Amount: sdk.NewInt(10000000), }, + 600, ) providerKeeper.SetParams(ctx, newParams) params = providerKeeper.GetParams(ctx) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 89d71ff344..b0e2845502 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -253,6 +253,8 @@ func (k Keeper) MakeConsumerGenesis( return false }) + var bondedValidators []stakingtypes.Validator + initialUpdates := []abci.ValidatorUpdate{} for _, p := range lastPowers { addr, err := sdk.ValAddressFromBech32(p.Address) @@ -274,10 +276,16 @@ func (k Keeper) MakeConsumerGenesis( PubKey: tmProtoPk, Power: p.Power, }) + + // gather all the bonded validators in order to construct the consumer validator set for consumer chain `chainID` + bondedValidators = append(bondedValidators, val) } - // Apply key assignments to the initial valset. - initialUpdatesWithConsumerKeys := k.MustApplyKeyAssignmentToValUpdates(ctx, chainID, initialUpdates) + nextValidators := k.ComputeNextEpochConsumerValSet(ctx, chainID, bondedValidators) + k.SetConsumerValSet(ctx, chainID, nextValidators) + + // get the initial updates with the latest set consumer public keys + initialUpdatesWithConsumerKeys := DiffValidators([]types.ConsumerValidator{}, nextValidators) // Get a hash of the consumer validator set from the update with applied consumer assigned keys updatesAsValSet, err := tmtypes.PB2TM.ValidatorUpdates(initialUpdatesWithConsumerKeys) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index e78823899a..fc1c7a4344 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -775,6 +775,7 @@ func TestMakeConsumerGenesis(t *testing.T) { Denom: "stake", Amount: sdk.NewInt(1000000), }, + BlocksPerEpoch: 600, } providerKeeper.SetParams(ctx, moduleParams) defer ctrl.Finish() diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 59fec69534..939f6d3995 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -148,13 +148,17 @@ func (k Keeper) EndBlockVSU(ctx sdk.Context) { // notify the staking module to complete all matured unbonding ops k.completeMaturedUnbondingOps(ctx) - // collect validator updates - k.QueueVSCPackets(ctx) + if ctx.BlockHeight()%k.GetBlocksPerEpoch(ctx) == 0 { + // only queue and send VSCPackets at the boundaries of an epoch - // try sending VSC packets to all registered consumer chains; - // if the CCV channel is not established for a consumer chain, - // the updates will remain queued until the channel is established - k.SendVSCPackets(ctx) + // collect validator updates + k.QueueVSCPackets(ctx) + + // try sending VSC packets to all registered consumer chains; + // if the CCV channel is not established for a consumer chain, + // the updates will remain queued until the channel is established + k.SendVSCPackets(ctx) + } } // SendVSCPackets iterates over all registered consumers and sends pending @@ -212,14 +216,15 @@ func (k Keeper) SendVSCPacketsToChain(ctx sdk.Context, chainID, channelID string // QueueVSCPackets queues latest validator updates for every registered consumer chain func (k Keeper) QueueVSCPackets(ctx sdk.Context) { valUpdateID := k.GetValidatorSetUpdateId(ctx) // current valset update ID - // Get the validator updates from the staking module. - // Note: GetValidatorUpdates panics if the updates provided by the x/staking module - // of cosmos-sdk is invalid. - stakingValUpdates := k.stakingKeeper.GetValidatorUpdates(ctx) + + // get the bonded validators from the staking module + bondedValidators := k.stakingKeeper.GetLastValidators(ctx) for _, chain := range k.GetAllConsumerChains(ctx) { - // Apply the key assignment to the validator updates. - valUpdates := k.MustApplyKeyAssignmentToValUpdates(ctx, chain.ChainId, stakingValUpdates) + currentValidators := k.GetConsumerValSet(ctx, chain.ChainId) + nextValidators := k.ComputeNextEpochConsumerValSet(ctx, chain.ChainId, bondedValidators) + valUpdates := DiffValidators(currentValidators, nextValidators) + k.SetConsumerValSet(ctx, chain.ChainId, nextValidators) // check whether there are changes in the validator set; // note that this also entails unbonding operations diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 02df262d53..b7e89b3dc2 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -1,23 +1,24 @@ package keeper_test import ( + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + ibctesting "github.com/cosmos/ibc-go/v7/testing" "strings" "testing" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v7/testing" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" "cosmossdk.io/math" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" @@ -66,16 +67,7 @@ func TestQueueVSCPackets(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mocks := testkeeper.NewMockedKeepers(ctrl) - mockStakingKeeper := mocks.MockStakingKeeper - - mockUpdates := []abci.ValidatorUpdate{} - if len(tc.packets) != 0 { - mockUpdates = tc.packets[0].ValidatorUpdates - } - - gomock.InOrder( - mockStakingKeeper.EXPECT().GetValidatorUpdates(gomock.Eq(ctx)).Return(mockUpdates), - ) + mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Times(1) pk := testkeeper.NewInMemProviderKeeper(keeperParams, mocks) // no-op if tc.packets is empty @@ -661,3 +653,47 @@ func TestOnAcknowledgementPacketWithAckError(t *testing.T) { testkeeper.TestProviderStateIsCleanedAfterConsumerChainIsStopped(t, ctx, providerKeeper, "chainID", "channelID") require.NoError(t, err) } + +// TestEndBlockVSU tests that during `EndBlockVSU`, we only queue VSC packets at the boundaries of an epoch +func TestEndBlockVSU(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // 10 blocks constitute an epoch + params := providertypes.DefaultParams() + params.BlocksPerEpoch = 10 + providerKeeper.SetParams(ctx, params) + + // create 4 sample lastValidators + var lastValidators []stakingtypes.Validator + for i := 0; i < 4; i++ { + lastValidators = append(lastValidators, crypto.NewCryptoIdentityFromIntSeed(i).SDKStakingValidator()) + } + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return(lastValidators).AnyTimes() + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(2)).AnyTimes() + + // set a sample client for a consumer chain so that `GetAllConsumerChains` in `QueueVSCPackets` iterates at least once + providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + + // with block height of 1 we do not expect any queueing of VSC packets + ctx = ctx.WithBlockHeight(1) + providerKeeper.EndBlockVSU(ctx) + require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + + // with block height of 5 we do not expect any queueing of VSC packets + ctx = ctx.WithBlockHeight(5) + providerKeeper.EndBlockVSU(ctx) + require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + + // with block height of 10 we expect the queueing of one VSC packet + ctx = ctx.WithBlockHeight(10) + providerKeeper.EndBlockVSU(ctx) + require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + + // With block height of 15 we expect no additional queueing of a VSC packet. + // Note that the pending VSC packet is still there because `SendVSCPackets` does not send the packet. We + // need to mock channels, etc. for this to work, and it's out of scope for this test. + ctx = ctx.WithBlockHeight(15) + providerKeeper.EndBlockVSU(ctx) + require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) +} diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go new file mode 100644 index 0000000000..71238d210d --- /dev/null +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -0,0 +1,178 @@ +package keeper + +import ( + "fmt" + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" +) + +// SetConsumerValidator sets provided consumer `validator` on the consumer chain with `chainID` +func (k Keeper) SetConsumerValidator( + ctx sdk.Context, + chainID string, + validator types.ConsumerValidator, +) { + store := ctx.KVStore(k.storeKey) + bz, err := validator.Marshal() + if err != nil { + panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err)) + } + + store.Set(types.ConsumerValidatorKey(chainID, validator.ProviderConsAddr), bz) +} + +// DeleteConsumerValidator removes consumer validator with `providerAddr` address +func (k Keeper) DeleteConsumerValidator( + ctx sdk.Context, + chainID string, + providerConsAddr types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.ConsumerValidatorKey(chainID, providerConsAddr.ToSdkConsAddr())) +} + +// DeleteConsumerValSet deletes all the stored consumer validators for chain `chainID` +func (k Keeper) DeleteConsumerValSet( + ctx sdk.Context, + chainID string) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.ConsumerValidatorBytePrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + + var keysToDel [][]byte + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) + } + for _, delKey := range keysToDel { + store.Delete(delKey) + } +} + +// IsConsumerValidator returns `true` if the consumer validator with `providerAddr` exists for chain `chainID` +// and `false` otherwise +func (k Keeper) IsConsumerValidator( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) bool { + store := ctx.KVStore(k.storeKey) + return store.Get(types.ConsumerValidatorKey(chainID, providerAddr.ToSdkConsAddr())) != nil +} + +// GetConsumerValSet returns all the consumer validators for chain `chainID` +func (k Keeper) GetConsumerValSet( + ctx sdk.Context, + chainID string) (validators []types.ConsumerValidator) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.ConsumerValidatorBytePrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + iterator.Value() + var validator types.ConsumerValidator + if err := validator.Unmarshal(iterator.Value()); err != nil { + panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err)) + } + validators = append(validators, validator) + } + + return validators +} + +// ComputeNextEpochConsumerValSet returns the next validator set that is responsible for validating consumer +// chain `chainID`, based on the bonded validators. +func (k Keeper) ComputeNextEpochConsumerValSet( + ctx sdk.Context, + chainID string, + bondedValidators []stakingtypes.Validator, +) []types.ConsumerValidator { + var nextValidators []types.ConsumerValidator + for _, val := range bondedValidators { + // get next voting power and the next consumer public key + nextPower := k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()) + consAddr, err := val.GetConsAddr() + if err != nil { + // this should never happen but is recoverable if we exclude this validator from the `nextValidators` + k.Logger(ctx).Error("could not get consensus address of validator", + "validator", val.GetOperator().String(), + "error", err) + continue + } + nextConsumerPublicKey, foundConsumerPublicKey := k.GetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(consAddr)) + if !foundConsumerPublicKey { + // if no consumer key assigned then use the validator's key itself + k.Logger(ctx).Info("could not retrieve public key for validator on consumer chain because"+ + " the validator did not assign a new consumer key", + "validator", val.GetOperator().String(), + "chainID", chainID) + nextConsumerPublicKey, err = val.TmConsPublicKey() + if err != nil { + // this should never happen and might not be recoverable because without the public key + // we cannot generate a validator update + panic(fmt.Errorf("could not retrieve validator's (%+v) public key: %w", val, err)) + } + } + + nextValidator := types.ConsumerValidator{ + ProviderConsAddr: consAddr, + Power: nextPower, + ConsumerPublicKey: &nextConsumerPublicKey, + } + nextValidators = append(nextValidators, nextValidator) + } + + return nextValidators +} + +// DiffValidators compares the current and the next epoch's consumer validators and returns the `ValidatorUpdate` diff +// needed by CometBFT to update the validator set on a chain. +func DiffValidators( + currentValidators []types.ConsumerValidator, + nextValidators []types.ConsumerValidator) []abci.ValidatorUpdate { + var updates []abci.ValidatorUpdate + + isCurrentValidator := make(map[string]types.ConsumerValidator) + for _, val := range currentValidators { + isCurrentValidator[val.ConsumerPublicKey.String()] = val + } + + isNextValidator := make(map[string]types.ConsumerValidator) + for _, val := range nextValidators { + isNextValidator[val.ConsumerPublicKey.String()] = val + } + + for _, currentVal := range currentValidators { + if nextVal, found := isNextValidator[currentVal.ConsumerPublicKey.String()]; !found { + // this consumer public key does not appear in the next validators and hence we remove the validator + // with that consumer public key by creating an update with 0 power + updates = append(updates, abci.ValidatorUpdate{PubKey: *currentVal.ConsumerPublicKey, Power: 0}) + } else if currentVal.Power != nextVal.Power { + // validator did not modify its consumer public key but has changed its voting power, so we + // have to create an update with the new power + updates = append(updates, abci.ValidatorUpdate{PubKey: *nextVal.ConsumerPublicKey, Power: nextVal.Power}) + } + // else no update is needed because neither the consumer public key changed, nor the power of the validator + } + + for _, nextVal := range nextValidators { + if _, found := isCurrentValidator[nextVal.ConsumerPublicKey.String()]; !found { + // this consumer public key does not exist in the current validators and hence we introduce this validator + updates = append(updates, abci.ValidatorUpdate{PubKey: *nextVal.ConsumerPublicKey, Power: nextVal.Power}) + } + } + + return updates +} + +// SetConsumerValSet resets the current consumer validators with the `nextValidators` computed by +// `ComputeNextEpochConsumerValSet` and hence this method should only be called after `ComputeNextEpochConsumerValSet` has completed. +func (k Keeper) SetConsumerValSet(ctx sdk.Context, chainID string, nextValidators []types.ConsumerValidator) { + k.DeleteConsumerValSet(ctx, chainID) + for _, val := range nextValidators { + k.SetConsumerValidator(ctx, chainID, val) + } +} diff --git a/x/ccv/provider/keeper/validator_set_update_test.go b/x/ccv/provider/keeper/validator_set_update_test.go new file mode 100644 index 0000000000..8505158816 --- /dev/null +++ b/x/ccv/provider/keeper/validator_set_update_test.go @@ -0,0 +1,355 @@ +package keeper_test + +import ( + "bytes" + "sort" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/proto/tendermint/crypto" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/stretchr/testify/require" +) + +// TestConsumerValidator tests the `SetConsumerValidator`, `IsConsumerValidator`, and `DeleteConsumerValidator` methods +func TestConsumerValidator(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validator := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddr"), + Power: 2, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + require.False(t, providerKeeper.IsConsumerValidator(ctx, "chainID", types.NewProviderConsAddress(validator.ProviderConsAddr))) + providerKeeper.SetConsumerValidator(ctx, "chainID", validator) + require.True(t, providerKeeper.IsConsumerValidator(ctx, "chainID", types.NewProviderConsAddress(validator.ProviderConsAddr))) + providerKeeper.DeleteConsumerValidator(ctx, "chainID", types.NewProviderConsAddress(validator.ProviderConsAddr)) + require.False(t, providerKeeper.IsConsumerValidator(ctx, "chainID", types.NewProviderConsAddress(validator.ProviderConsAddr))) +} + +func TestGetConsumerValSet(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // create 3 validators and set them as current validators + expectedValidators := []types.ConsumerValidator{ + { + ProviderConsAddr: []byte("providerConsAddr1"), + Power: 1, + ConsumerPublicKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte{1}, + }, + }, + }, + { + ProviderConsAddr: []byte("providerConsAddr2"), + Power: 2, + ConsumerPublicKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte{2}, + }, + }, + }, + { + ProviderConsAddr: []byte("providerConsAddr3"), + Power: 3, + ConsumerPublicKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte{3}, + }, + }, + }, + } + + for _, expectedValidator := range expectedValidators { + providerKeeper.SetConsumerValidator(ctx, "chainID", + types.ConsumerValidator{ + ProviderConsAddr: expectedValidator.ProviderConsAddr, + Power: expectedValidator.Power, + ConsumerPublicKey: expectedValidator.ConsumerPublicKey, + }) + } + + actualValidators := providerKeeper.GetConsumerValSet(ctx, "chainID") + + // sort validators first to be able to compare + sortValidators := func(validators []types.ConsumerValidator) { + sort.Slice(validators, func(i int, j int) bool { + return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 + }) + } + sortValidators(expectedValidators) + sortValidators(actualValidators) + require.Equal(t, expectedValidators, actualValidators) +} + +// createConsumerValidator is a helper function to create a consumer validator with the given `power`. It uses `index` as +// the `ProviderConsAddr` of the validator, and the `seed` to generate the consumer public key. Returns the validator +// and its consumer public key. +func createConsumerValidator(index int, power int64, seed int) (types.ConsumerValidator, crypto.PublicKey) { + publicKey := cryptotestutil.NewCryptoIdentityFromIntSeed(seed).TMProtoCryptoPublicKey() + + return types.ConsumerValidator{ + ProviderConsAddr: []byte{byte(index)}, + Power: power, + ConsumerPublicKey: &publicKey, + }, publicKey +} + +func TestComputeNextEpochConsumerValSet(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // helper function to generate a validator with the given power and with a provider address based on index + createStakingValidator := func(ctx sdk.Context, mocks testkeeper.MockedKeepers, index int, power int64) stakingtypes.Validator { + providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{byte(index)}).PubKey() + consAddr := sdk.ConsAddress(providerConsPubKey.Address()) + providerAddr := types.NewProviderConsAddress(consAddr) + pk, _ := cryptocodec.FromTmPubKeyInterface(providerConsPubKey) + pkAny, _ := codectypes.NewAnyWithValue(pk) + + var providerValidatorAddr sdk.ValAddress + providerValidatorAddr = providerAddr.Address.Bytes() + + mocks.MockStakingKeeper.EXPECT(). + GetLastValidatorPower(ctx, providerValidatorAddr).Return(power).AnyTimes() + + return stakingtypes.Validator{ + OperatorAddress: providerValidatorAddr.String(), + ConsensusPubkey: pkAny, + } + } + + // no consumer validators returned if we have no bonded validators + require.Empty(t, providerKeeper.ComputeNextEpochConsumerValSet(ctx, chainID, []stakingtypes.Validator{})) + + var expectedValidators []types.ConsumerValidator + + // create a staking validator A that has not set a consumer public key + valA := createStakingValidator(ctx, mocks, 1, 1) + // because validator A has no consumer key set, the `ConsumerPublicKey` we expect is the key on the provider chain + valAConsAddr, _ := valA.GetConsAddr() + valAPublicKey, _ := valA.TmConsPublicKey() + expectedValidators = append(expectedValidators, types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valAConsAddr).Address.Bytes(), + Power: 1, + ConsumerPublicKey: &valAPublicKey, + }) + + // create a staking validator B that has set a consumer public key + valB := createStakingValidator(ctx, mocks, 2, 2) + // validator B has set a consumer key, the `ConsumerPublicKey` we expect is the key set by `SetValidatorConsumerPubKey` + valBConsumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() + valBConsAddr, _ := valB.GetConsAddr() + providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(valBConsAddr), valBConsumerKey) + expectedValidators = append(expectedValidators, types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valBConsAddr).Address.Bytes(), + Power: 2, + ConsumerPublicKey: &valBConsumerKey, + }) + + bondedValidators := []stakingtypes.Validator{valA, valB} + actualValidators := providerKeeper.ComputeNextEpochConsumerValSet(ctx, "chainID", bondedValidators) + require.Equal(t, expectedValidators, actualValidators) +} + +func TestDiff(t *testing.T) { + // In what follows we create 6 validators: A, B, C, D, E, and F where currentValidators = {A, B, C, D, E} + // and nextValidators = {B, C, D, E, F}. For the validators {B, C, D, E} in the intersection we have: + // - validator B has no power or consumer key change + // - validator C has changed its power + // - validator D has no power change but has changed its consumer key + // - validator E has both changed its power and its consumer key + + var expectedUpdates []abci.ValidatorUpdate + + // validator A only exists in `currentValidators` and hence an update with 0 power would be generated + // to remove this validator + currentA, currentPublicKeyA := createConsumerValidator(1, 1, 1) + expectedUpdates = append(expectedUpdates, abci.ValidatorUpdate{PubKey: currentPublicKeyA, Power: 0}) + + // validator B exists in both `currentValidators` and `nextValidators` but it did not change its + // power or consumer public key and hence no validator update is generated + currentB, _ := createConsumerValidator(2, 1, 2) + nextB, _ := createConsumerValidator(2, 1, 2) + + // validator C exists in both `currentValidators` and `nextValidators` and it changes its power, so + // a validator update is generated with the new power + currentC, currentPublicKeyC := createConsumerValidator(3, 1, 3) + nextC, _ := createConsumerValidator(3, 2, 3) + expectedUpdates = append(expectedUpdates, abci.ValidatorUpdate{PubKey: currentPublicKeyC, Power: 2}) + + // validator D exists in both `currentValidators` and `nextValidators` and it changes its consumer public key, so + // a validator update is generated to remove the old public key and another update to add the new public key + currentD, currentPublicKeyD := createConsumerValidator(4, 1, 4) + nextD, nextPublicKeyD := createConsumerValidator(4, 1, 5) + expectedUpdates = append(expectedUpdates, abci.ValidatorUpdate{PubKey: currentPublicKeyD, Power: 0}) + expectedUpdates = append(expectedUpdates, abci.ValidatorUpdate{PubKey: nextPublicKeyD, Power: 1}) + + // validator E exists in both `currentValidators` and `nextValidators` and it changes both its power and + // its consumer public key, so a validator update is generated to remove the old public key and another update to + // add the new public key with thew new power + currentE, currentPublicKeyE := createConsumerValidator(5, 1, 6) + nextE, nextPublicKeyE := createConsumerValidator(5, 2, 7) + expectedUpdates = append(expectedUpdates, abci.ValidatorUpdate{PubKey: currentPublicKeyE, Power: 0}) + expectedUpdates = append(expectedUpdates, abci.ValidatorUpdate{PubKey: nextPublicKeyE, Power: 2}) + + // validator F does not exist in `currentValidators` and hence an update is generated to add this new validator + nextF, nextPublicKeyF := createConsumerValidator(6, 1, 8) + expectedUpdates = append(expectedUpdates, abci.ValidatorUpdate{PubKey: nextPublicKeyF, Power: 1}) + + currentValidators := []types.ConsumerValidator{currentA, currentB, currentC, currentD, currentE} + nextValidators := []types.ConsumerValidator{nextB, nextC, nextD, nextE, nextF} + + actualUpdates := keeper.DiffValidators(currentValidators, nextValidators) + + // sort validators first to be able to compare + sortUpdates := func(updates []abci.ValidatorUpdate) { + sort.Slice(updates, func(i, j int) bool { + if updates[i].Power != updates[j].Power { + return updates[i].Power < updates[j].Power + } + return updates[i].PubKey.String() < updates[j].PubKey.String() + }) + } + + sortUpdates(expectedUpdates) + sortUpdates(actualUpdates) + require.Equal(t, expectedUpdates, actualUpdates) +} + +func TestDiffEdgeCases(t *testing.T) { + require.Empty(t, len(keeper.DiffValidators([]types.ConsumerValidator{}, []types.ConsumerValidator{}))) + + valA, publicKeyA := createConsumerValidator(1, 1, 1) + valB, publicKeyB := createConsumerValidator(2, 2, 2) + valC, publicKeyC := createConsumerValidator(3, 3, 3) + validators := []types.ConsumerValidator{valA, valB, valC} + + // we do not expect any validator updates if the `currentValidators` are the same with the `nextValidators` + require.Empty(t, len(keeper.DiffValidators(validators, validators))) + + // only have `nextValidators` that would generate validator updates for the validators to be added + expectedUpdates := []abci.ValidatorUpdate{{publicKeyA, 1}, {publicKeyB, 2}, {publicKeyC, 3}} + actualUpdates := keeper.DiffValidators([]types.ConsumerValidator{}, validators) + // sort validators first to be able to compare + sortUpdates := func(updates []abci.ValidatorUpdate) { + sort.Slice(updates, func(i, j int) bool { + if updates[i].Power != updates[j].Power { + return updates[i].Power < updates[j].Power + } + return updates[i].PubKey.String() < updates[j].PubKey.String() + }) + } + + sortUpdates(expectedUpdates) + sortUpdates(actualUpdates) + require.Equal(t, expectedUpdates, actualUpdates) + + // only have `currentValidators` that would generate validator updates for the validators to be removed + expectedUpdates = []abci.ValidatorUpdate{{publicKeyA, 0}, {publicKeyB, 0}, {publicKeyC, 0}} + actualUpdates = keeper.DiffValidators(validators, []types.ConsumerValidator{}) + sortUpdates(expectedUpdates) + sortUpdates(actualUpdates) + require.Equal(t, expectedUpdates, actualUpdates) + + // have nonempty `currentValidators` and `nextValidators`, but with empty intersection + // all old validators should be removed, all new validators should be added + expectedUpdates = []abci.ValidatorUpdate{{publicKeyA, 0}, {publicKeyB, 2}} + actualUpdates = keeper.DiffValidators(validators[0:1], validators[1:2]) + sortUpdates(expectedUpdates) + sortUpdates(actualUpdates) + require.Equal(t, expectedUpdates, actualUpdates) +} + +func TestSetConsumerValSet(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + currentValidators := []types.ConsumerValidator{ + { + ProviderConsAddr: []byte("currentProviderConsAddr1"), + Power: 2, + ConsumerPublicKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte{2}, + }, + }, + }, + { + ProviderConsAddr: []byte("currentProviderConsAddr2"), + Power: 3, + ConsumerPublicKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte{3}, + }, + }, + }, + { + ProviderConsAddr: []byte("currentProviderConsAddr3"), + Power: 4, + ConsumerPublicKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte{4}, + }, + }, + }, + } + + nextValidators := []types.ConsumerValidator{ + { + ProviderConsAddr: []byte("nextProviderConsAddr1"), + Power: 2, + ConsumerPublicKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte{2}, + }, + }, + }, + { + ProviderConsAddr: []byte("nextProviderConsAddr2"), + Power: 3, + ConsumerPublicKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte{3}, + }, + }, + }, + } + + // set the `currentValidators` for chain `chainID` + require.Empty(t, providerKeeper.GetConsumerValSet(ctx, chainID)) + for _, validator := range currentValidators { + providerKeeper.SetConsumerValidator(ctx, chainID, validator) + } + require.NotEmpty(t, providerKeeper.GetConsumerValSet(ctx, chainID)) + + providerKeeper.SetConsumerValSet(ctx, chainID, nextValidators) + nextCurrentValidators := providerKeeper.GetConsumerValSet(ctx, chainID) + + // sort validators first to be able to compare + sortValidators := func(validators []types.ConsumerValidator) { + sort.Slice(validators, func(i, j int) bool { + return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 + }) + } + + sortValidators(nextValidators) + sortValidators(nextCurrentValidators) + require.Equal(t, nextValidators, nextCurrentValidators) +} diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index 45d766fcfb..41a716757f 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -81,7 +81,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -102,7 +102,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -123,7 +123,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -144,7 +144,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -171,7 +171,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -198,7 +198,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -225,7 +225,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(1000000)}), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(1000000)}, 600), nil, nil, nil, @@ -252,7 +252,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -279,7 +279,7 @@ func TestValidateGenesisState(t *testing.T) { 0, // 0 vsc timeout here types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -306,7 +306,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, 0, // 0 slash meter replenish period here types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -333,7 +333,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, "1.15", - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -685,7 +685,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600), nil, nil, nil, @@ -706,7 +706,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-1000000)}), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-1000000)}, 600), nil, nil, nil, diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 615b901368..e9222ade98 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -120,7 +120,9 @@ const ( // on consumer chains to validator addresses on the provider chain ValidatorsByConsumerAddrBytePrefix - // KeyAssignmentReplacementsBytePrefix is the byte prefix that will store the key assignments that need to be replaced in the current block + // KeyAssignmentReplacementsBytePrefix was the byte prefix used to store the key assignments that needed to be replaced in the current block + // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations + // [DEPRECATED] KeyAssignmentReplacementsBytePrefix // ConsumerAddrsToPruneBytePrefix is the byte prefix that will store the mapping from VSC ids @@ -145,6 +147,9 @@ const ( // ProposedConsumerChainByteKey is the byte prefix storing the consumer chainId in consumerAddition gov proposal submitted before voting finishes ProposedConsumerChainByteKey + // ConsumerValidatorBytePrefix is the byte prefix used when storing for each consumer chain all the consumer validators in this epoch + ConsumerValidatorBytePrefix + // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go ) @@ -362,12 +367,6 @@ func ValidatorsByConsumerAddrKey(chainID string, addr ConsumerConsAddress) []byt return ChainIdAndConsAddrKey(ValidatorsByConsumerAddrBytePrefix, chainID, addr.ToSdkConsAddr()) } -// KeyAssignmentReplacementsKey returns the key under which the -// key assignments that need to be replaced in the current block are stored -func KeyAssignmentReplacementsKey(chainID string, addr ProviderConsAddress) []byte { - return ChainIdAndConsAddrKey(KeyAssignmentReplacementsBytePrefix, chainID, addr.ToSdkConsAddr()) -} - // ConsumerAddrsToPruneKey returns the key under which the // mapping from VSC ids to consumer validators addresses is stored func ConsumerAddrsToPruneKey(chainID string, vscID uint64) []byte { @@ -517,6 +516,12 @@ func ParseProposedConsumerChainKey(prefix byte, bz []byte) (uint64, error) { return proposalID, nil } +// ConsumerValidatorKey returns the key of consumer chain `chainID` and validator with `providerAddr` +func ConsumerValidatorKey(chainID string, providerAddr []byte) []byte { + prefix := ChainIdWithLenKey(ConsumerValidatorBytePrefix, chainID) + return append(prefix, providerAddr...) +} + // // End of generic helpers section // diff --git a/x/ccv/provider/types/keys_test.go b/x/ccv/provider/types/keys_test.go index 4d5ea58ff8..02faa9a640 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -56,6 +56,7 @@ func getAllKeyPrefixes() []byte { providertypes.VSCMaturedHandledThisBlockBytePrefix, providertypes.EquivocationEvidenceMinHeightBytePrefix, providertypes.ProposedConsumerChainByteKey, + providertypes.ConsumerValidatorBytePrefix, } } @@ -96,7 +97,6 @@ func getAllFullyDefinedKeys() [][]byte { providertypes.GlobalSlashEntryKey(providertypes.GlobalSlashEntry{}), providertypes.ConsumerValidatorsKey("chainID", providertypes.NewProviderConsAddress([]byte{0x05})), providertypes.ValidatorsByConsumerAddrKey("chainID", providertypes.NewConsumerConsAddress([]byte{0x05})), - providertypes.KeyAssignmentReplacementsKey("chainID", providertypes.NewProviderConsAddress([]byte{0x05})), providertypes.ConsumerAddrsToPruneKey("chainID", 88), providertypes.SlashLogKey(providertypes.NewProviderConsAddress([]byte{0x05})), providertypes.VSCMaturedHandledThisBlockKey(), diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index a580e60f41..a90468b04f 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -36,6 +36,14 @@ const ( // that is replenished to the slash meter every replenish period. This param also serves as a maximum // fraction of total voting power that the slash meter can hold. DefaultSlashMeterReplenishFraction = "0.05" + + // DefaultBlocksPerEpoch defines the default blocks that constitute an epoch. Assuming we need 6 seconds per block, + // an epoch corresponds to 1 hour (6 * 600 = 3600 seconds). + DefaultBlocksPerEpoch = 600 + + // MaxBlocksPerEpoch defines the maximum blocks that constitute an epoch. Assuming we need 6 seconds per block, + // the maximum epoch corresponds to 2 hours (6 * 1200 = 7200 seconds). + MaxBlocksPerEpoch = 1200 ) // Reflection based keys for params subspace @@ -47,6 +55,7 @@ var ( KeySlashMeterReplenishPeriod = []byte("SlashMeterReplenishPeriod") KeySlashMeterReplenishFraction = []byte("SlashMeterReplenishFraction") KeyConsumerRewardDenomRegistrationFee = []byte("ConsumerRewardDenomRegistrationFee") + KeyBlocksPerEpoch = []byte("BlocksPerEpoch") ) // ParamKeyTable returns a key table with the necessary registered provider params @@ -64,6 +73,7 @@ func NewParams( slashMeterReplenishPeriod time.Duration, slashMeterReplenishFraction string, consumerRewardDenomRegistrationFee sdk.Coin, + blocksPerEpoch int64, ) Params { return Params{ TemplateClient: cs, @@ -74,6 +84,7 @@ func NewParams( SlashMeterReplenishPeriod: slashMeterReplenishPeriod, SlashMeterReplenishFraction: slashMeterReplenishFraction, ConsumerRewardDenomRegistrationFee: consumerRewardDenomRegistrationFee, + BlocksPerEpoch: blocksPerEpoch, } } @@ -104,6 +115,7 @@ func DefaultParams() Params { Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(10000000), }, + DefaultBlocksPerEpoch, ) } @@ -136,6 +148,9 @@ func (p Params) Validate() error { if err := ValidateCoin(p.ConsumerRewardDenomRegistrationFee); err != nil { return fmt.Errorf("consumer reward denom registration fee is invalid: %s", err) } + if err := ValidateBlocksPerEpoch(p.BlocksPerEpoch); err != nil { + return fmt.Errorf("blocks per epoch is invalid: %s", err) + } return nil } @@ -150,6 +165,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { paramtypes.NewParamSetPair(KeySlashMeterReplenishPeriod, p.SlashMeterReplenishPeriod, ccvtypes.ValidateDuration), paramtypes.NewParamSetPair(KeySlashMeterReplenishFraction, p.SlashMeterReplenishFraction, ccvtypes.ValidateStringFraction), paramtypes.NewParamSetPair(KeyConsumerRewardDenomRegistrationFee, p.ConsumerRewardDenomRegistrationFee, ValidateCoin), + paramtypes.NewParamSetPair(KeyBlocksPerEpoch, p.BlocksPerEpoch, ccvtypes.ValidatePositiveInt64), } } @@ -192,3 +208,17 @@ func ValidateCoin(i interface{}) error { return nil } + +// ValidateBlocksPerEpoch validates the BlocksPerEpoch param is in [1, MaxBlocksPerEpoch] +func ValidateBlocksPerEpoch(i interface{}) error { + if _, ok := i.(int64); !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + if i.(int64) <= int64(0) { + return fmt.Errorf("blocks per epoch must be positive") + } + if i.(int64) > MaxBlocksPerEpoch { + return fmt.Errorf("blocks per epoch have to be at most %d", MaxBlocksPerEpoch) + } + return nil +} diff --git a/x/ccv/provider/types/params_test.go b/x/ccv/provider/types/params_test.go index 1de6b6fe54..cc5810a7ce 100644 --- a/x/ccv/provider/types/params_test.go +++ b/x/ccv/provider/types/params_test.go @@ -24,39 +24,48 @@ func TestValidateParams(t *testing.T) { {"custom valid params", types.NewParams( ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), true}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), true}, {"custom invalid params", types.NewParams( ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, 0, clienttypes.Height{}, nil, []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), false}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, {"blank client", types.NewParams(&ibctmtypes.ClientState{}, - "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), false}, - {"nil client", types.NewParams(nil, "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), false}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, + {"nil client", types.NewParams(nil, "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, // Check if "0.00" is valid or if a zero dec TrustFraction needs to return an error {"0 trusting period fraction", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), true}, + "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), true}, {"0 ccv timeout period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", 0, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), false}, + "0.33", 0, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, {"0 init timeout period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, 0, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), false}, + "0.33", time.Hour, 0, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, {"0 vsc timeout period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 0, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), false}, + "0.33", time.Hour, time.Hour, 0, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, {"0 slash meter replenish period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, 0, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, 0, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, {"slash meter replenish fraction over 1", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "1.5", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "1.5", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, {"invalid consumer reward denom registration fee denom", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 1000), false}, {"invalid consumer reward denom registration fee amount", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-10000000)}), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-10000000)}, 1000), false}, + {"0 blocks per epoch", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, + time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 0), false}, + {"exceeding max blocks per epoch", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, + time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, types.MaxBlocksPerEpoch+1), false}, + {"valid blocks per epoch", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, + time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, types.MaxBlocksPerEpoch), true}, } for _, tc := range testCases { diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index 41a87e69f4..819154b75d 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -451,6 +451,8 @@ type Params struct { SlashMeterReplenishFraction string `protobuf:"bytes,7,opt,name=slash_meter_replenish_fraction,json=slashMeterReplenishFraction,proto3" json:"slash_meter_replenish_fraction,omitempty"` // The fee required to be paid to add a reward denom ConsumerRewardDenomRegistrationFee types2.Coin `protobuf:"bytes,9,opt,name=consumer_reward_denom_registration_fee,json=consumerRewardDenomRegistrationFee,proto3" json:"consumer_reward_denom_registration_fee"` + // The number of blocks that comprise an epoch. + BlocksPerEpoch int64 `protobuf:"varint,10,opt,name=blocks_per_epoch,json=blocksPerEpoch,proto3" json:"blocks_per_epoch,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -542,6 +544,13 @@ func (m *Params) GetConsumerRewardDenomRegistrationFee() types2.Coin { return types2.Coin{} } +func (m *Params) GetBlocksPerEpoch() int64 { + if m != nil { + return m.BlocksPerEpoch + } + return 0 +} + // SlashAcks contains cons addresses of consumer chain validators // successfully slashed on the provider chain. type SlashAcks struct { @@ -1385,6 +1394,71 @@ func (m *ConsumerAddrsToPrune) GetConsumerAddrs() *AddressList { return nil } +// ConsumerValidator is used to facilitate epoch-based transitions. It contains relevant info for +// a validator that is opted in, in an epoch, on a consumer chain. +type ConsumerValidator struct { + // validator's consensus address on the provider chain + ProviderConsAddr []byte `protobuf:"bytes,1,opt,name=provider_cons_addr,json=providerConsAddr,proto3" json:"provider_cons_addr,omitempty"` + // voting power the validator has during this epoch + Power int64 `protobuf:"varint,2,opt,name=power,proto3" json:"power,omitempty"` + // public key the validator uses on the consumer chain during this epoch + ConsumerPublicKey *crypto.PublicKey `protobuf:"bytes,3,opt,name=consumer_public_key,json=consumerPublicKey,proto3" json:"consumer_public_key,omitempty"` +} + +func (m *ConsumerValidator) Reset() { *m = ConsumerValidator{} } +func (m *ConsumerValidator) String() string { return proto.CompactTextString(m) } +func (*ConsumerValidator) ProtoMessage() {} +func (*ConsumerValidator) Descriptor() ([]byte, []int) { + return fileDescriptor_f22ec409a72b7b72, []int{22} +} +func (m *ConsumerValidator) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsumerValidator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsumerValidator.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 *ConsumerValidator) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsumerValidator.Merge(m, src) +} +func (m *ConsumerValidator) XXX_Size() int { + return m.Size() +} +func (m *ConsumerValidator) XXX_DiscardUnknown() { + xxx_messageInfo_ConsumerValidator.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsumerValidator proto.InternalMessageInfo + +func (m *ConsumerValidator) GetProviderConsAddr() []byte { + if m != nil { + return m.ProviderConsAddr + } + return nil +} + +func (m *ConsumerValidator) GetPower() int64 { + if m != nil { + return m.Power + } + return 0 +} + +func (m *ConsumerValidator) GetConsumerPublicKey() *crypto.PublicKey { + if m != nil { + return m.ConsumerPublicKey + } + return nil +} + func init() { proto.RegisterType((*ConsumerAdditionProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerAdditionProposal") proto.RegisterType((*ConsumerRemovalProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerRemovalProposal") @@ -1408,6 +1482,7 @@ func init() { proto.RegisterType((*ValidatorConsumerPubKey)(nil), "interchain_security.ccv.provider.v1.ValidatorConsumerPubKey") proto.RegisterType((*ValidatorByConsumerAddr)(nil), "interchain_security.ccv.provider.v1.ValidatorByConsumerAddr") proto.RegisterType((*ConsumerAddrsToPrune)(nil), "interchain_security.ccv.provider.v1.ConsumerAddrsToPrune") + proto.RegisterType((*ConsumerValidator)(nil), "interchain_security.ccv.provider.v1.ConsumerValidator") } func init() { @@ -1415,113 +1490,117 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1694 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x72, 0x1b, 0xc7, - 0x11, 0xe6, 0x12, 0x20, 0x45, 0x34, 0xf8, 0xa7, 0x25, 0x6d, 0x2d, 0x15, 0x06, 0xa4, 0xd6, 0xb1, - 0xc3, 0x94, 0xcb, 0x8b, 0x90, 0x4e, 0xaa, 0x5c, 0xaa, 0xb8, 0x5c, 0x24, 0x28, 0x5b, 0x14, 0x63, - 0x8b, 0x5e, 0x32, 0x54, 0x25, 0x39, 0x6c, 0x0d, 0x66, 0x47, 0xc0, 0x14, 0x17, 0x3b, 0xab, 0x99, - 0xd9, 0x95, 0x71, 0xc9, 0x39, 0x47, 0xe7, 0xe6, 0xca, 0x25, 0x4e, 0x5e, 0x20, 0xe7, 0xbc, 0x81, - 0x8f, 0x3e, 0xe6, 0x64, 0xa7, 0xa4, 0x63, 0x5e, 0x22, 0x35, 0xb3, 0xff, 0x20, 0xa1, 0x40, 0xe5, - 0xe4, 0x36, 0xdb, 0xd3, 0xfd, 0x75, 0xcf, 0x74, 0xf7, 0xd7, 0x03, 0xc0, 0x01, 0x0d, 0x25, 0xe1, - 0x78, 0x88, 0x68, 0xe8, 0x09, 0x82, 0x63, 0x4e, 0xe5, 0xb8, 0x8b, 0x71, 0xd2, 0x8d, 0x38, 0x4b, - 0xa8, 0x4f, 0x78, 0x37, 0xd9, 0x2f, 0xd6, 0x4e, 0xc4, 0x99, 0x64, 0xe6, 0x5b, 0x37, 0xd8, 0x38, - 0x18, 0x27, 0x4e, 0xa1, 0x97, 0xec, 0xdf, 0x7d, 0x7b, 0x1a, 0x70, 0xb2, 0xdf, 0x7d, 0x4e, 0x39, - 0x49, 0xb1, 0xee, 0x6e, 0x0e, 0xd8, 0x80, 0xe9, 0x65, 0x57, 0xad, 0x32, 0xe9, 0xce, 0x80, 0xb1, - 0x41, 0x40, 0xba, 0xfa, 0xab, 0x1f, 0x3f, 0xed, 0x4a, 0x3a, 0x22, 0x42, 0xa2, 0x51, 0x94, 0x29, - 0x74, 0x26, 0x15, 0xfc, 0x98, 0x23, 0x49, 0x59, 0x98, 0x03, 0xd0, 0x3e, 0xee, 0x62, 0xc6, 0x49, - 0x17, 0x07, 0x94, 0x84, 0x52, 0x79, 0x4d, 0x57, 0x99, 0x42, 0x57, 0x29, 0x04, 0x74, 0x30, 0x94, - 0xa9, 0x58, 0x74, 0x25, 0x09, 0x7d, 0xc2, 0x47, 0x34, 0x55, 0x2e, 0xbf, 0x32, 0x83, 0xed, 0xca, - 0x3e, 0xe6, 0xe3, 0x48, 0xb2, 0xee, 0x15, 0x19, 0x8b, 0x6c, 0xf7, 0x1d, 0xcc, 0xc4, 0x88, 0x89, - 0x2e, 0x51, 0xe7, 0x0f, 0x31, 0xe9, 0x26, 0xfb, 0x7d, 0x22, 0xd1, 0x7e, 0x21, 0xc8, 0xe3, 0xce, - 0xf4, 0xfa, 0x48, 0x94, 0x3a, 0x98, 0xd1, 0x2c, 0x6e, 0xfb, 0xfb, 0x45, 0xb0, 0x7a, 0x2c, 0x14, - 0xf1, 0x88, 0xf0, 0x43, 0xdf, 0xa7, 0xea, 0x48, 0x67, 0x9c, 0x45, 0x4c, 0xa0, 0xc0, 0xdc, 0x84, - 0x05, 0x49, 0x65, 0x40, 0x2c, 0x63, 0xd7, 0xd8, 0x6b, 0xb9, 0xe9, 0x87, 0xb9, 0x0b, 0x6d, 0x9f, - 0x08, 0xcc, 0x69, 0xa4, 0x94, 0xad, 0x79, 0xbd, 0x57, 0x15, 0x99, 0x5b, 0xb0, 0x94, 0xe6, 0x81, - 0xfa, 0x56, 0x43, 0x6f, 0xdf, 0xd2, 0xdf, 0x27, 0xbe, 0xf9, 0x09, 0xac, 0xd2, 0x90, 0x4a, 0x8a, - 0x02, 0x6f, 0x48, 0xd4, 0x6d, 0x58, 0xcd, 0x5d, 0x63, 0xaf, 0x7d, 0x70, 0xd7, 0xa1, 0x7d, 0xec, - 0xa8, 0x0b, 0x74, 0xb2, 0x6b, 0x4b, 0xf6, 0x9d, 0x87, 0x5a, 0xe3, 0xa8, 0xf9, 0xcd, 0x77, 0x3b, - 0x73, 0xee, 0x4a, 0x66, 0x97, 0x0a, 0xcd, 0x7b, 0xb0, 0x3c, 0x20, 0x21, 0x11, 0x54, 0x78, 0x43, - 0x24, 0x86, 0xd6, 0xc2, 0xae, 0xb1, 0xb7, 0xec, 0xb6, 0x33, 0xd9, 0x43, 0x24, 0x86, 0xe6, 0x0e, - 0xb4, 0xfb, 0x34, 0x44, 0x7c, 0x9c, 0x6a, 0x2c, 0x6a, 0x0d, 0x48, 0x45, 0x5a, 0xa1, 0x07, 0x20, - 0x22, 0xf4, 0x3c, 0xf4, 0x54, 0xb6, 0xad, 0x5b, 0x59, 0x20, 0x69, 0xa6, 0x9d, 0x3c, 0xd3, 0xce, - 0x45, 0x5e, 0x0a, 0x47, 0x4b, 0x2a, 0x90, 0x2f, 0xbf, 0xdf, 0x31, 0xdc, 0x96, 0xb6, 0x53, 0x3b, - 0xe6, 0x67, 0xb0, 0x1e, 0x87, 0x7d, 0x16, 0xfa, 0x34, 0x1c, 0x78, 0x11, 0xe1, 0x94, 0xf9, 0xd6, - 0x92, 0x86, 0xda, 0xba, 0x06, 0x75, 0x9c, 0x15, 0x4d, 0x8a, 0xf4, 0x95, 0x42, 0x5a, 0x2b, 0x8c, - 0xcf, 0xb4, 0xad, 0xf9, 0x39, 0x98, 0x18, 0x27, 0x3a, 0x24, 0x16, 0xcb, 0x1c, 0xb1, 0x35, 0x3b, - 0xe2, 0x3a, 0xc6, 0xc9, 0x45, 0x6a, 0x9d, 0x41, 0xfe, 0x1e, 0xee, 0x48, 0x8e, 0x42, 0xf1, 0x94, - 0xf0, 0x49, 0x5c, 0x98, 0x1d, 0xf7, 0x8d, 0x1c, 0xa3, 0x0e, 0xfe, 0x10, 0x76, 0x71, 0x56, 0x40, - 0x1e, 0x27, 0x3e, 0x15, 0x92, 0xd3, 0x7e, 0xac, 0x6c, 0xbd, 0xa7, 0x1c, 0x61, 0x5d, 0x23, 0x6d, - 0x5d, 0x04, 0x9d, 0x5c, 0xcf, 0xad, 0xa9, 0x7d, 0x9c, 0x69, 0x99, 0x8f, 0xe1, 0x27, 0xfd, 0x80, - 0xe1, 0x2b, 0xa1, 0x82, 0xf3, 0x6a, 0x48, 0xda, 0xf5, 0x88, 0x0a, 0xa1, 0xd0, 0x96, 0x77, 0x8d, - 0xbd, 0x86, 0x7b, 0x2f, 0xd5, 0x3d, 0x23, 0xfc, 0xb8, 0xa2, 0x79, 0x51, 0x51, 0x34, 0xdf, 0x03, - 0x73, 0x48, 0x85, 0x64, 0x9c, 0x62, 0x14, 0x78, 0x24, 0x94, 0x9c, 0x12, 0x61, 0xad, 0x68, 0xf3, - 0xdb, 0xe5, 0xce, 0x83, 0x74, 0xc3, 0x7c, 0x04, 0xf7, 0xa6, 0x3a, 0xf5, 0xf0, 0x10, 0x85, 0x21, - 0x09, 0xac, 0x55, 0x7d, 0x94, 0x1d, 0x7f, 0x8a, 0xcf, 0x5e, 0xaa, 0x76, 0x7f, 0xe9, 0x8f, 0x5f, - 0xef, 0xcc, 0x7d, 0xf5, 0xf5, 0xce, 0x9c, 0xfd, 0x77, 0x03, 0xee, 0xf4, 0x8a, 0x83, 0x8f, 0x58, - 0x82, 0x82, 0xff, 0x67, 0x83, 0x1d, 0x42, 0x4b, 0x48, 0x16, 0xa5, 0x25, 0xdd, 0x7c, 0x8d, 0x92, - 0x5e, 0x52, 0x66, 0x6a, 0xc3, 0xfe, 0x8b, 0x01, 0x9b, 0x0f, 0x9e, 0xc5, 0x34, 0x61, 0x18, 0xfd, - 0x4f, 0xf8, 0xe0, 0x14, 0x56, 0x48, 0x05, 0x4f, 0x58, 0x8d, 0xdd, 0xc6, 0x5e, 0xfb, 0xe0, 0x6d, - 0x27, 0x25, 0x27, 0xa7, 0xe0, 0xac, 0x8c, 0xa0, 0x9c, 0xaa, 0x77, 0xb7, 0x6e, 0x7b, 0x7f, 0xde, - 0x32, 0xec, 0xbf, 0x19, 0x70, 0x57, 0xdd, 0xf4, 0x80, 0xb8, 0xe4, 0x39, 0xe2, 0xfe, 0x31, 0x09, - 0xd9, 0x48, 0xfc, 0xe0, 0x38, 0x6d, 0x58, 0xf1, 0x35, 0x92, 0x27, 0x99, 0x87, 0x7c, 0x5f, 0xc7, - 0xa9, 0x75, 0x94, 0xf0, 0x82, 0x1d, 0xfa, 0xbe, 0xb9, 0x07, 0xeb, 0xa5, 0x0e, 0x57, 0xf9, 0x54, - 0xd7, 0xac, 0xd4, 0x56, 0x73, 0x35, 0x9d, 0x65, 0x62, 0xff, 0xdb, 0x80, 0xf5, 0x4f, 0x02, 0xd6, - 0x47, 0xc1, 0x79, 0x80, 0xc4, 0x50, 0x55, 0xd9, 0x58, 0xa5, 0x87, 0x93, 0xac, 0xbd, 0x75, 0x78, - 0x33, 0xa7, 0x47, 0x99, 0x69, 0xc2, 0xf9, 0x08, 0x6e, 0x17, 0x0d, 0x57, 0x54, 0x81, 0x3e, 0xcd, - 0xd1, 0xc6, 0x8b, 0xef, 0x76, 0xd6, 0xf2, 0x62, 0xeb, 0xe9, 0x8a, 0x38, 0x76, 0xd7, 0x70, 0x4d, - 0xe0, 0x9b, 0x1d, 0x68, 0xd3, 0x3e, 0xf6, 0x04, 0x79, 0xe6, 0x85, 0xf1, 0x48, 0x17, 0x50, 0xd3, - 0x6d, 0xd1, 0x3e, 0x3e, 0x27, 0xcf, 0x3e, 0x8b, 0x47, 0xe6, 0xfb, 0xf0, 0x66, 0x3e, 0x58, 0xbd, - 0x04, 0x05, 0x9e, 0xb2, 0x57, 0xd7, 0xc1, 0x75, 0x3d, 0x2d, 0xbb, 0x1b, 0xf9, 0xee, 0x25, 0x0a, - 0x94, 0xb3, 0x43, 0xdf, 0xe7, 0xf6, 0x3f, 0x16, 0x60, 0xf1, 0x0c, 0x71, 0x34, 0x12, 0xe6, 0x05, - 0xac, 0x49, 0x32, 0x8a, 0x02, 0x24, 0x89, 0x97, 0x92, 0x79, 0x76, 0xd2, 0x77, 0x35, 0xc9, 0x57, - 0x87, 0xa0, 0x53, 0x19, 0x7b, 0xc9, 0xbe, 0xd3, 0xd3, 0xd2, 0x73, 0x89, 0x24, 0x71, 0x57, 0x73, - 0x8c, 0x54, 0x68, 0x7e, 0x00, 0x96, 0xe4, 0xb1, 0x90, 0x25, 0xcd, 0x96, 0xfc, 0x92, 0xe6, 0xf2, - 0xcd, 0x7c, 0x3f, 0x65, 0xa6, 0x82, 0x57, 0x6e, 0x66, 0xd4, 0xc6, 0x0f, 0x61, 0xd4, 0x73, 0xd8, - 0x50, 0xe3, 0x68, 0x12, 0xb3, 0x39, 0x3b, 0xe6, 0x6d, 0x65, 0x5f, 0x07, 0xfd, 0x1c, 0xcc, 0x44, - 0xe0, 0x49, 0xcc, 0x85, 0xd7, 0x88, 0x33, 0x11, 0xb8, 0x0e, 0xe9, 0xc3, 0xb6, 0x50, 0xc5, 0xe7, - 0x8d, 0x88, 0xd4, 0xfc, 0x1c, 0x05, 0x24, 0xa4, 0x62, 0x98, 0x83, 0x2f, 0xce, 0x0e, 0xbe, 0xa5, - 0x81, 0x3e, 0x55, 0x38, 0x6e, 0x0e, 0x93, 0x79, 0xe9, 0x41, 0xe7, 0x66, 0x2f, 0x45, 0x82, 0x6e, - 0xe9, 0x04, 0xfd, 0xe8, 0x06, 0x88, 0x22, 0x4b, 0x02, 0xde, 0xa9, 0xcc, 0x11, 0xd5, 0xd5, 0x9e, - 0x6e, 0x28, 0x8f, 0x93, 0x81, 0x22, 0x5b, 0x94, 0x8e, 0x14, 0x42, 0x8a, 0x59, 0x98, 0xb1, 0x87, - 0x7a, 0xda, 0x14, 0xcc, 0xd1, 0x63, 0x34, 0xcc, 0x1e, 0x0c, 0x76, 0x39, 0x6e, 0x0a, 0x8e, 0x70, - 0x2b, 0x58, 0x1f, 0x13, 0xf2, 0xa8, 0xb9, 0xb4, 0xb4, 0xde, 0xb2, 0x7f, 0x06, 0x2d, 0xdd, 0xa2, - 0x87, 0xf8, 0x4a, 0x98, 0xdb, 0xd0, 0x52, 0xb5, 0x4e, 0x84, 0x20, 0xc2, 0x32, 0x74, 0x67, 0x97, - 0x02, 0x5b, 0xc2, 0xd6, 0xb4, 0xe7, 0x92, 0x30, 0x9f, 0xc0, 0xad, 0x88, 0xe8, 0x59, 0xae, 0x0d, - 0xdb, 0x07, 0x1f, 0x3a, 0x33, 0xbc, 0x5c, 0x9d, 0x69, 0x80, 0x6e, 0x8e, 0x66, 0xf3, 0xf2, 0x91, - 0x36, 0x31, 0x42, 0x84, 0x79, 0x39, 0xe9, 0xf4, 0x57, 0xaf, 0xe5, 0x74, 0x02, 0xaf, 0xf4, 0xf9, - 0x2e, 0xb4, 0x0f, 0xd3, 0x63, 0xff, 0x9a, 0x0a, 0x79, 0xfd, 0x5a, 0x96, 0xab, 0xd7, 0xf2, 0x08, - 0x56, 0xb3, 0xc9, 0x77, 0xc1, 0x34, 0xcd, 0x98, 0x3f, 0x06, 0xc8, 0x46, 0xa6, 0xa2, 0xa7, 0x94, - 0x88, 0x5b, 0x99, 0xe4, 0xc4, 0xaf, 0x4d, 0xb0, 0xf9, 0xda, 0x04, 0xb3, 0x5d, 0x58, 0xbb, 0x14, - 0xf8, 0x37, 0xf9, 0xb3, 0xe8, 0x71, 0x24, 0xcc, 0x37, 0x60, 0x51, 0x75, 0x46, 0x06, 0xd4, 0x74, - 0x17, 0x12, 0x81, 0x4f, 0x34, 0x17, 0x97, 0x4f, 0x2f, 0x16, 0x79, 0xd4, 0x17, 0xd6, 0xfc, 0x6e, - 0x63, 0xaf, 0xe9, 0xae, 0xc6, 0xa5, 0xf9, 0x89, 0x2f, 0xec, 0xdf, 0x42, 0xbb, 0x02, 0x68, 0xae, - 0xc2, 0x7c, 0x81, 0x35, 0x4f, 0x7d, 0xf3, 0x3e, 0x6c, 0x95, 0x40, 0x75, 0x72, 0x4d, 0x11, 0x5b, - 0xee, 0x9d, 0x42, 0xa1, 0xc6, 0xaf, 0xc2, 0x7e, 0x0c, 0x9b, 0x27, 0x65, 0x2b, 0x17, 0xd4, 0x5d, - 0x3b, 0xa1, 0x51, 0x9f, 0xd1, 0xdb, 0xd0, 0x2a, 0x7e, 0x5f, 0xe8, 0xd3, 0x37, 0xdd, 0x52, 0x60, - 0x8f, 0x60, 0xfd, 0x52, 0xe0, 0x73, 0x12, 0xfa, 0x25, 0xd8, 0x94, 0x0b, 0x38, 0x9a, 0x04, 0x9a, - 0xf9, 0xfd, 0x5a, 0xba, 0x63, 0xb0, 0x75, 0x89, 0x02, 0xea, 0x23, 0xc9, 0xf8, 0x39, 0x91, 0xe9, - 0x58, 0x3d, 0x43, 0xf8, 0x8a, 0x48, 0x61, 0xba, 0xd0, 0x0c, 0xa8, 0x90, 0x59, 0x65, 0x7d, 0x30, - 0xb5, 0xb2, 0x92, 0x7d, 0x67, 0x1a, 0xc8, 0x31, 0x92, 0x28, 0xeb, 0x48, 0x8d, 0x65, 0xff, 0x14, - 0x36, 0x3e, 0x45, 0x32, 0xe6, 0xc4, 0xaf, 0xe5, 0x78, 0x1d, 0x1a, 0x2a, 0x7f, 0x86, 0xce, 0x9f, - 0x5a, 0xaa, 0x29, 0x6f, 0x3d, 0xf8, 0x22, 0x62, 0x5c, 0x12, 0xff, 0xda, 0x8d, 0xbc, 0xe2, 0x7a, - 0xaf, 0x60, 0x43, 0x5d, 0x96, 0x20, 0xa1, 0xef, 0x15, 0xe7, 0x4c, 0xf3, 0xd8, 0x3e, 0xf8, 0xe5, - 0x4c, 0xdd, 0x31, 0xe9, 0x2e, 0x3b, 0xc0, 0xed, 0x64, 0x42, 0x2e, 0xec, 0x3f, 0x19, 0x60, 0x9d, - 0x92, 0xf1, 0xa1, 0x10, 0x74, 0x10, 0x8e, 0x48, 0x28, 0x15, 0xb3, 0x21, 0x4c, 0xd4, 0xd2, 0x7c, - 0x0b, 0x56, 0x8a, 0x49, 0xaa, 0x07, 0xa8, 0xa1, 0x07, 0xe8, 0x72, 0x2e, 0x54, 0x0d, 0x66, 0xde, - 0x07, 0x88, 0x38, 0x49, 0x3c, 0xec, 0x5d, 0x91, 0x71, 0x96, 0xc5, 0xed, 0xea, 0x60, 0x4c, 0x7f, - 0xfd, 0x39, 0x67, 0x71, 0x3f, 0xa0, 0xf8, 0x94, 0x8c, 0xdd, 0x25, 0xa5, 0xdf, 0x3b, 0x25, 0x63, - 0xf5, 0xd2, 0x89, 0xd8, 0x73, 0xc2, 0xf5, 0x34, 0x6b, 0xb8, 0xe9, 0x87, 0xfd, 0x67, 0x03, 0xee, - 0x14, 0xe9, 0xc8, 0xcb, 0xf5, 0x2c, 0xee, 0x2b, 0x8b, 0x57, 0xdc, 0xdb, 0xb5, 0x68, 0xe7, 0x6f, - 0x88, 0xf6, 0x23, 0x58, 0x2e, 0x1a, 0x44, 0xc5, 0xdb, 0x98, 0x21, 0xde, 0x76, 0x6e, 0x71, 0x4a, - 0xc6, 0xf6, 0x1f, 0x2a, 0xb1, 0x1d, 0x8d, 0x2b, 0xdc, 0xc7, 0xff, 0x4b, 0x6c, 0x85, 0xdb, 0x6a, - 0x6c, 0xb8, 0x6a, 0x7f, 0xed, 0x00, 0x8d, 0xeb, 0x07, 0xb0, 0xff, 0x6a, 0xc0, 0x66, 0xd5, 0xab, - 0xb8, 0x60, 0x67, 0x3c, 0x0e, 0xc9, 0xab, 0xbc, 0x97, 0xed, 0x37, 0x5f, 0x6d, 0xbf, 0x27, 0xb0, - 0x5a, 0x0b, 0x4a, 0x64, 0xb7, 0xf1, 0xf3, 0x99, 0x6a, 0xac, 0xc2, 0xae, 0xee, 0x4a, 0xf5, 0x1c, - 0xe2, 0xe8, 0xc9, 0x37, 0x2f, 0x3a, 0xc6, 0xb7, 0x2f, 0x3a, 0xc6, 0xbf, 0x5e, 0x74, 0x8c, 0x2f, - 0x5f, 0x76, 0xe6, 0xbe, 0x7d, 0xd9, 0x99, 0xfb, 0xe7, 0xcb, 0xce, 0xdc, 0xef, 0x3e, 0x1c, 0x50, - 0x39, 0x8c, 0xfb, 0x0e, 0x66, 0xa3, 0x6e, 0xf6, 0xd3, 0xbe, 0xf4, 0xf5, 0x5e, 0xf1, 0xbf, 0x47, - 0xf2, 0x8b, 0xee, 0x17, 0xf5, 0x7f, 0x55, 0xe4, 0x38, 0x22, 0xa2, 0xbf, 0xa8, 0x59, 0xe1, 0xfd, - 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x9f, 0x08, 0x80, 0x3d, 0x86, 0x11, 0x00, 0x00, + // 1755 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4f, 0x6f, 0x1b, 0xc7, + 0x15, 0xd7, 0x92, 0x94, 0x2c, 0x3e, 0x4a, 0x94, 0xb4, 0x52, 0xe2, 0x95, 0xab, 0x52, 0xf2, 0xa6, + 0x49, 0x55, 0xa4, 0x59, 0x56, 0x4a, 0x0b, 0x04, 0x46, 0x83, 0x40, 0xa2, 0x9c, 0x58, 0x56, 0x12, + 0x2b, 0x2b, 0x55, 0x46, 0xdb, 0xc3, 0x62, 0x38, 0x3b, 0x26, 0x07, 0x5a, 0xee, 0xac, 0x67, 0x66, + 0xd7, 0xe1, 0xa5, 0xe7, 0x1e, 0xd3, 0x5b, 0xd0, 0x4b, 0xd3, 0x02, 0x3d, 0xf7, 0x6b, 0xe4, 0x98, + 0x63, 0x4f, 0x49, 0x61, 0x1f, 0xfb, 0x25, 0x8a, 0x99, 0xfd, 0x4b, 0x4a, 0x72, 0x69, 0xb8, 0xbd, + 0xcd, 0xbe, 0x79, 0xef, 0xf7, 0xfe, 0xbf, 0x37, 0x24, 0xec, 0xd3, 0x50, 0x12, 0x8e, 0x87, 0x88, + 0x86, 0x9e, 0x20, 0x38, 0xe6, 0x54, 0x8e, 0xbb, 0x18, 0x27, 0xdd, 0x88, 0xb3, 0x84, 0xfa, 0x84, + 0x77, 0x93, 0xbd, 0xe2, 0xec, 0x44, 0x9c, 0x49, 0x66, 0xbe, 0x75, 0x8d, 0x8c, 0x83, 0x71, 0xe2, + 0x14, 0x7c, 0xc9, 0xde, 0x9d, 0xb7, 0x6f, 0x02, 0x4e, 0xf6, 0xba, 0xcf, 0x28, 0x27, 0x29, 0xd6, + 0x9d, 0x8d, 0x01, 0x1b, 0x30, 0x7d, 0xec, 0xaa, 0x53, 0x46, 0xdd, 0x1e, 0x30, 0x36, 0x08, 0x48, + 0x57, 0x7f, 0xf5, 0xe3, 0x27, 0x5d, 0x49, 0x47, 0x44, 0x48, 0x34, 0x8a, 0x32, 0x86, 0xce, 0x34, + 0x83, 0x1f, 0x73, 0x24, 0x29, 0x0b, 0x73, 0x00, 0xda, 0xc7, 0x5d, 0xcc, 0x38, 0xe9, 0xe2, 0x80, + 0x92, 0x50, 0x2a, 0xad, 0xe9, 0x29, 0x63, 0xe8, 0x2a, 0x86, 0x80, 0x0e, 0x86, 0x32, 0x25, 0x8b, + 0xae, 0x24, 0xa1, 0x4f, 0xf8, 0x88, 0xa6, 0xcc, 0xe5, 0x57, 0x26, 0xb0, 0x55, 0xb9, 0xc7, 0x7c, + 0x1c, 0x49, 0xd6, 0xbd, 0x24, 0x63, 0x91, 0xdd, 0xbe, 0x83, 0x99, 0x18, 0x31, 0xd1, 0x25, 0xca, + 0xff, 0x10, 0x93, 0x6e, 0xb2, 0xd7, 0x27, 0x12, 0xed, 0x15, 0x84, 0xdc, 0xee, 0x8c, 0xaf, 0x8f, + 0x44, 0xc9, 0x83, 0x19, 0xcd, 0xec, 0xb6, 0x7f, 0x58, 0x00, 0xab, 0xc7, 0x42, 0x11, 0x8f, 0x08, + 0x3f, 0xf0, 0x7d, 0xaa, 0x5c, 0x3a, 0xe5, 0x2c, 0x62, 0x02, 0x05, 0xe6, 0x06, 0xcc, 0x4b, 0x2a, + 0x03, 0x62, 0x19, 0x3b, 0xc6, 0x6e, 0xd3, 0x4d, 0x3f, 0xcc, 0x1d, 0x68, 0xf9, 0x44, 0x60, 0x4e, + 0x23, 0xc5, 0x6c, 0xd5, 0xf4, 0x5d, 0x95, 0x64, 0x6e, 0xc2, 0x62, 0x9a, 0x07, 0xea, 0x5b, 0x75, + 0x7d, 0x7d, 0x4b, 0x7f, 0x1f, 0xfb, 0xe6, 0x27, 0xd0, 0xa6, 0x21, 0x95, 0x14, 0x05, 0xde, 0x90, + 0xa8, 0x68, 0x58, 0x8d, 0x1d, 0x63, 0xb7, 0xb5, 0x7f, 0xc7, 0xa1, 0x7d, 0xec, 0xa8, 0x00, 0x3a, + 0x59, 0xd8, 0x92, 0x3d, 0xe7, 0x81, 0xe6, 0x38, 0x6c, 0x7c, 0xfb, 0xfd, 0xf6, 0x9c, 0xbb, 0x9c, + 0xc9, 0xa5, 0x44, 0xf3, 0x2e, 0x2c, 0x0d, 0x48, 0x48, 0x04, 0x15, 0xde, 0x10, 0x89, 0xa1, 0x35, + 0xbf, 0x63, 0xec, 0x2e, 0xb9, 0xad, 0x8c, 0xf6, 0x00, 0x89, 0xa1, 0xb9, 0x0d, 0xad, 0x3e, 0x0d, + 0x11, 0x1f, 0xa7, 0x1c, 0x0b, 0x9a, 0x03, 0x52, 0x92, 0x66, 0xe8, 0x01, 0x88, 0x08, 0x3d, 0x0b, + 0x3d, 0x95, 0x6d, 0xeb, 0x56, 0x66, 0x48, 0x9a, 0x69, 0x27, 0xcf, 0xb4, 0x73, 0x9e, 0x97, 0xc2, + 0xe1, 0xa2, 0x32, 0xe4, 0xab, 0x1f, 0xb6, 0x0d, 0xb7, 0xa9, 0xe5, 0xd4, 0x8d, 0xf9, 0x39, 0xac, + 0xc6, 0x61, 0x9f, 0x85, 0x3e, 0x0d, 0x07, 0x5e, 0x44, 0x38, 0x65, 0xbe, 0xb5, 0xa8, 0xa1, 0x36, + 0xaf, 0x40, 0x1d, 0x65, 0x45, 0x93, 0x22, 0x7d, 0xad, 0x90, 0x56, 0x0a, 0xe1, 0x53, 0x2d, 0x6b, + 0x7e, 0x01, 0x26, 0xc6, 0x89, 0x36, 0x89, 0xc5, 0x32, 0x47, 0x6c, 0xce, 0x8e, 0xb8, 0x8a, 0x71, + 0x72, 0x9e, 0x4a, 0x67, 0x90, 0xbf, 0x87, 0xdb, 0x92, 0xa3, 0x50, 0x3c, 0x21, 0x7c, 0x1a, 0x17, + 0x66, 0xc7, 0x7d, 0x23, 0xc7, 0x98, 0x04, 0x7f, 0x00, 0x3b, 0x38, 0x2b, 0x20, 0x8f, 0x13, 0x9f, + 0x0a, 0xc9, 0x69, 0x3f, 0x56, 0xb2, 0xde, 0x13, 0x8e, 0xb0, 0xae, 0x91, 0x96, 0x2e, 0x82, 0x4e, + 0xce, 0xe7, 0x4e, 0xb0, 0x7d, 0x9c, 0x71, 0x99, 0x8f, 0xe0, 0x27, 0xfd, 0x80, 0xe1, 0x4b, 0xa1, + 0x8c, 0xf3, 0x26, 0x90, 0xb4, 0xea, 0x11, 0x15, 0x42, 0xa1, 0x2d, 0xed, 0x18, 0xbb, 0x75, 0xf7, + 0x6e, 0xca, 0x7b, 0x4a, 0xf8, 0x51, 0x85, 0xf3, 0xbc, 0xc2, 0x68, 0xbe, 0x07, 0xe6, 0x90, 0x0a, + 0xc9, 0x38, 0xc5, 0x28, 0xf0, 0x48, 0x28, 0x39, 0x25, 0xc2, 0x5a, 0xd6, 0xe2, 0x6b, 0xe5, 0xcd, + 0xfd, 0xf4, 0xc2, 0x7c, 0x08, 0x77, 0x6f, 0x54, 0xea, 0xe1, 0x21, 0x0a, 0x43, 0x12, 0x58, 0x6d, + 0xed, 0xca, 0xb6, 0x7f, 0x83, 0xce, 0x5e, 0xca, 0x76, 0x6f, 0xf1, 0x8f, 0xdf, 0x6c, 0xcf, 0x7d, + 0xfd, 0xcd, 0xf6, 0x9c, 0xfd, 0x0f, 0x03, 0x6e, 0xf7, 0x0a, 0xc7, 0x47, 0x2c, 0x41, 0xc1, 0xff, + 0xb3, 0xc1, 0x0e, 0xa0, 0x29, 0x24, 0x8b, 0xd2, 0x92, 0x6e, 0xbc, 0x42, 0x49, 0x2f, 0x2a, 0x31, + 0x75, 0x61, 0xff, 0xc5, 0x80, 0x8d, 0xfb, 0x4f, 0x63, 0x9a, 0x30, 0x8c, 0xfe, 0x27, 0xf3, 0xe0, + 0x04, 0x96, 0x49, 0x05, 0x4f, 0x58, 0xf5, 0x9d, 0xfa, 0x6e, 0x6b, 0xff, 0x6d, 0x27, 0x1d, 0x4e, + 0x4e, 0x31, 0xb3, 0xb2, 0x01, 0xe5, 0x54, 0xb5, 0xbb, 0x93, 0xb2, 0xf7, 0x6a, 0x96, 0x61, 0xff, + 0xcd, 0x80, 0x3b, 0x2a, 0xd2, 0x03, 0xe2, 0x92, 0x67, 0x88, 0xfb, 0x47, 0x24, 0x64, 0x23, 0xf1, + 0xda, 0x76, 0xda, 0xb0, 0xec, 0x6b, 0x24, 0x4f, 0x32, 0x0f, 0xf9, 0xbe, 0xb6, 0x53, 0xf3, 0x28, + 0xe2, 0x39, 0x3b, 0xf0, 0x7d, 0x73, 0x17, 0x56, 0x4b, 0x1e, 0xae, 0xf2, 0xa9, 0xc2, 0xac, 0xd8, + 0xda, 0x39, 0x9b, 0xce, 0x32, 0xb1, 0xff, 0x6d, 0xc0, 0xea, 0x27, 0x01, 0xeb, 0xa3, 0xe0, 0x2c, + 0x40, 0x62, 0xa8, 0xaa, 0x6c, 0xac, 0xd2, 0xc3, 0x49, 0xd6, 0xde, 0xda, 0xbc, 0x99, 0xd3, 0xa3, + 0xc4, 0xf4, 0xc0, 0xf9, 0x08, 0xd6, 0x8a, 0x86, 0x2b, 0xaa, 0x40, 0x7b, 0x73, 0xb8, 0xfe, 0xfc, + 0xfb, 0xed, 0x95, 0xbc, 0xd8, 0x7a, 0xba, 0x22, 0x8e, 0xdc, 0x15, 0x3c, 0x41, 0xf0, 0xcd, 0x0e, + 0xb4, 0x68, 0x1f, 0x7b, 0x82, 0x3c, 0xf5, 0xc2, 0x78, 0xa4, 0x0b, 0xa8, 0xe1, 0x36, 0x69, 0x1f, + 0x9f, 0x91, 0xa7, 0x9f, 0xc7, 0x23, 0xf3, 0x7d, 0x78, 0x33, 0x5f, 0xac, 0x5e, 0x82, 0x02, 0x4f, + 0xc9, 0xab, 0x70, 0x70, 0x5d, 0x4f, 0x4b, 0xee, 0x7a, 0x7e, 0x7b, 0x81, 0x02, 0xa5, 0xec, 0xc0, + 0xf7, 0xb9, 0xfd, 0x62, 0x1e, 0x16, 0x4e, 0x11, 0x47, 0x23, 0x61, 0x9e, 0xc3, 0x8a, 0x24, 0xa3, + 0x28, 0x40, 0x92, 0x78, 0xe9, 0x30, 0xcf, 0x3c, 0x7d, 0x57, 0x0f, 0xf9, 0xea, 0x12, 0x74, 0x2a, + 0x6b, 0x2f, 0xd9, 0x73, 0x7a, 0x9a, 0x7a, 0x26, 0x91, 0x24, 0x6e, 0x3b, 0xc7, 0x48, 0x89, 0xe6, + 0x07, 0x60, 0x49, 0x1e, 0x0b, 0x59, 0x8e, 0xd9, 0x72, 0xbe, 0xa4, 0xb9, 0x7c, 0x33, 0xbf, 0x4f, + 0x27, 0x53, 0x31, 0x57, 0xae, 0x9f, 0xa8, 0xf5, 0xd7, 0x99, 0xa8, 0x67, 0xb0, 0xae, 0xd6, 0xd1, + 0x34, 0x66, 0x63, 0x76, 0xcc, 0x35, 0x25, 0x3f, 0x09, 0xfa, 0x05, 0x98, 0x89, 0xc0, 0xd3, 0x98, + 0xf3, 0xaf, 0x60, 0x67, 0x22, 0xf0, 0x24, 0xa4, 0x0f, 0x5b, 0x42, 0x15, 0x9f, 0x37, 0x22, 0x52, + 0xcf, 0xe7, 0x28, 0x20, 0x21, 0x15, 0xc3, 0x1c, 0x7c, 0x61, 0x76, 0xf0, 0x4d, 0x0d, 0xf4, 0x99, + 0xc2, 0x71, 0x73, 0x98, 0x4c, 0x4b, 0x0f, 0x3a, 0xd7, 0x6b, 0x29, 0x12, 0x74, 0x4b, 0x27, 0xe8, + 0x47, 0xd7, 0x40, 0x14, 0x59, 0x12, 0xf0, 0x4e, 0x65, 0x8f, 0xa8, 0xae, 0xf6, 0x74, 0x43, 0x79, + 0x9c, 0x0c, 0xd4, 0xb0, 0x45, 0xe9, 0x4a, 0x21, 0xa4, 0xd8, 0x85, 0xd9, 0xf4, 0x50, 0x4f, 0x9b, + 0x62, 0x72, 0xf4, 0x18, 0x0d, 0xb3, 0x07, 0x83, 0x5d, 0xae, 0x9b, 0x62, 0x46, 0xb8, 0x15, 0xac, + 0x8f, 0x09, 0x51, 0xdd, 0x5c, 0x59, 0x39, 0x24, 0x62, 0x78, 0xa8, 0x57, 0x62, 0xdd, 0x6d, 0x17, + 0xeb, 0xe5, 0xbe, 0xa2, 0x3e, 0x6c, 0x2c, 0x2e, 0xae, 0x36, 0xed, 0x9f, 0x41, 0x53, 0x37, 0xf3, + 0x01, 0xbe, 0x14, 0xe6, 0x16, 0x34, 0x55, 0x57, 0x10, 0x21, 0x88, 0xb0, 0x0c, 0x3d, 0x03, 0x4a, + 0x82, 0x2d, 0x61, 0xf3, 0xa6, 0x87, 0x95, 0x30, 0x1f, 0xc3, 0xad, 0x88, 0xe8, 0xad, 0xaf, 0x05, + 0x5b, 0xfb, 0x1f, 0x3a, 0x33, 0xbc, 0x71, 0x9d, 0x9b, 0x00, 0xdd, 0x1c, 0xcd, 0xe6, 0xe5, 0x73, + 0x6e, 0x6a, 0xd9, 0x08, 0xf3, 0x62, 0x5a, 0xe9, 0xaf, 0x5f, 0x49, 0xe9, 0x14, 0x5e, 0xa9, 0xf3, + 0x5d, 0x68, 0x1d, 0xa4, 0x6e, 0x7f, 0x4a, 0x85, 0xbc, 0x1a, 0x96, 0xa5, 0x6a, 0x58, 0x1e, 0x42, + 0x3b, 0xdb, 0x91, 0xe7, 0x4c, 0x0f, 0x24, 0xf3, 0xc7, 0x00, 0xd9, 0x72, 0x55, 0x83, 0x2c, 0x1d, + 0xd9, 0xcd, 0x8c, 0x72, 0xec, 0x4f, 0xec, 0xba, 0xda, 0xc4, 0xae, 0xb3, 0x5d, 0x58, 0xb9, 0x10, + 0xf8, 0x37, 0xf9, 0x03, 0xea, 0x51, 0x24, 0xcc, 0x37, 0x60, 0x41, 0xf5, 0x50, 0x06, 0xd4, 0x70, + 0xe7, 0x13, 0x81, 0x8f, 0xf5, 0xd4, 0x2e, 0x1f, 0x69, 0x2c, 0xf2, 0xa8, 0x2f, 0xac, 0xda, 0x4e, + 0x7d, 0xb7, 0xe1, 0xb6, 0xe3, 0x52, 0xfc, 0xd8, 0x17, 0xf6, 0x6f, 0xa1, 0x55, 0x01, 0x34, 0xdb, + 0x50, 0x2b, 0xb0, 0x6a, 0xd4, 0x37, 0xef, 0xc1, 0x66, 0x09, 0x34, 0x39, 0x86, 0x53, 0xc4, 0xa6, + 0x7b, 0xbb, 0x60, 0x98, 0x98, 0xc4, 0xc2, 0x7e, 0x04, 0x1b, 0xc7, 0x65, 0xd3, 0x17, 0x43, 0x7e, + 0xc2, 0x43, 0x63, 0x72, 0x9b, 0x6f, 0x41, 0xb3, 0xf8, 0x25, 0xa2, 0xbd, 0x6f, 0xb8, 0x25, 0xc1, + 0x1e, 0xc1, 0xea, 0x85, 0xc0, 0x67, 0x24, 0xf4, 0x4b, 0xb0, 0x1b, 0x02, 0x70, 0x38, 0x0d, 0x34, + 0xf3, 0x4b, 0xb7, 0x54, 0xc7, 0x60, 0xf3, 0x02, 0x05, 0xd4, 0x47, 0x92, 0xf1, 0x33, 0x22, 0xd3, + 0x05, 0x7c, 0x8a, 0xf0, 0x25, 0x91, 0xc2, 0x74, 0xa1, 0x11, 0x50, 0x21, 0xb3, 0xca, 0xfa, 0xe0, + 0xc6, 0xca, 0x4a, 0xf6, 0x9c, 0x9b, 0x40, 0x8e, 0x90, 0x44, 0x59, 0xef, 0x6a, 0x2c, 0xfb, 0xa7, + 0xb0, 0xfe, 0x19, 0x92, 0x31, 0x27, 0xfe, 0x44, 0x8e, 0x57, 0xa1, 0xae, 0xf2, 0x67, 0xe8, 0xfc, + 0xa9, 0xa3, 0x7a, 0x0f, 0x58, 0xf7, 0xbf, 0x8c, 0x18, 0x97, 0xc4, 0xbf, 0x12, 0x91, 0x97, 0x84, + 0xf7, 0x12, 0xd6, 0x55, 0xb0, 0x04, 0x09, 0x7d, 0xaf, 0xf0, 0x33, 0xcd, 0x63, 0x6b, 0xff, 0x57, + 0x33, 0x75, 0xc7, 0xb4, 0xba, 0xcc, 0x81, 0xb5, 0x64, 0x8a, 0x2e, 0xec, 0x3f, 0x19, 0x60, 0x9d, + 0x90, 0xf1, 0x81, 0x10, 0x74, 0x10, 0x8e, 0x48, 0x28, 0xd5, 0x0c, 0x44, 0x98, 0xa8, 0xa3, 0xf9, + 0x16, 0x2c, 0x17, 0x3b, 0x57, 0xaf, 0x5a, 0x43, 0xaf, 0xda, 0xa5, 0x9c, 0xa8, 0x1a, 0xcc, 0xbc, + 0x07, 0x10, 0x71, 0x92, 0x78, 0xd8, 0xbb, 0x24, 0xe3, 0x2c, 0x8b, 0x5b, 0xd5, 0x15, 0x9a, 0xfe, + 0x4e, 0x74, 0x4e, 0xe3, 0x7e, 0x40, 0xf1, 0x09, 0x19, 0xbb, 0x8b, 0x8a, 0xbf, 0x77, 0x42, 0xc6, + 0xea, 0x4d, 0x14, 0xb1, 0x67, 0x84, 0xeb, 0xbd, 0x57, 0x77, 0xd3, 0x0f, 0xfb, 0xcf, 0x06, 0xdc, + 0x2e, 0xd2, 0x91, 0x97, 0xeb, 0x69, 0xdc, 0x57, 0x12, 0x2f, 0x89, 0xdb, 0x15, 0x6b, 0x6b, 0xd7, + 0x58, 0xfb, 0x11, 0x2c, 0x15, 0x0d, 0xa2, 0xec, 0xad, 0xcf, 0x60, 0x6f, 0x2b, 0x97, 0x38, 0x21, + 0x63, 0xfb, 0x0f, 0x15, 0xdb, 0x0e, 0xc7, 0x95, 0xd9, 0xc7, 0xff, 0x8b, 0x6d, 0x85, 0xda, 0xaa, + 0x6d, 0xb8, 0x2a, 0x7f, 0xc5, 0x81, 0xfa, 0x55, 0x07, 0xec, 0xbf, 0x1a, 0xb0, 0x51, 0xd5, 0x2a, + 0xce, 0xd9, 0x29, 0x8f, 0x43, 0xf2, 0x32, 0xed, 0x65, 0xfb, 0xd5, 0xaa, 0xed, 0xf7, 0x18, 0xda, + 0x13, 0x46, 0x89, 0x2c, 0x1a, 0xbf, 0x98, 0xa9, 0xc6, 0x2a, 0xd3, 0xd5, 0x5d, 0xae, 0xfa, 0x21, + 0xec, 0xbf, 0x1b, 0xb0, 0x96, 0xdb, 0x58, 0x04, 0xcb, 0xfc, 0x39, 0x98, 0x85, 0x7b, 0xe5, 0xeb, + 0x2d, 0x2d, 0xa9, 0xd5, 0xfc, 0x26, 0x7f, 0xba, 0x95, 0xa5, 0x51, 0xab, 0x94, 0x86, 0xf9, 0x29, + 0xac, 0x17, 0x26, 0x47, 0x3a, 0x41, 0x33, 0x67, 0xb1, 0x78, 0x9f, 0x16, 0xa4, 0xc3, 0xc7, 0xdf, + 0x3e, 0xef, 0x18, 0xdf, 0x3d, 0xef, 0x18, 0xff, 0x7a, 0xde, 0x31, 0xbe, 0x7a, 0xd1, 0x99, 0xfb, + 0xee, 0x45, 0x67, 0xee, 0x9f, 0x2f, 0x3a, 0x73, 0xbf, 0xfb, 0x70, 0x40, 0xe5, 0x30, 0xee, 0x3b, + 0x98, 0x8d, 0xba, 0xd9, 0x9f, 0x15, 0x65, 0x4c, 0xde, 0x2b, 0xfe, 0xc9, 0x49, 0x7e, 0xd9, 0xfd, + 0x72, 0xf2, 0x7f, 0x22, 0x39, 0x8e, 0x88, 0xe8, 0x2f, 0xe8, 0xe9, 0xf5, 0xfe, 0x7f, 0x02, 0x00, + 0x00, 0xff, 0xff, 0x0a, 0xef, 0x81, 0x2b, 0x58, 0x12, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -1876,6 +1955,11 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.BlocksPerEpoch != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.BlocksPerEpoch)) + i-- + dAtA[i] = 0x50 + } { size, err := m.ConsumerRewardDenomRegistrationFee.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -2585,6 +2669,53 @@ func (m *ConsumerAddrsToPrune) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ConsumerValidator) 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 *ConsumerValidator) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsumerValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ConsumerPublicKey != nil { + { + size, err := m.ConsumerPublicKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProvider(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Power != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.Power)) + i-- + dAtA[i] = 0x10 + } + if len(m.ProviderConsAddr) > 0 { + i -= len(m.ProviderConsAddr) + copy(dAtA[i:], m.ProviderConsAddr) + i = encodeVarintProvider(dAtA, i, uint64(len(m.ProviderConsAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintProvider(dAtA []byte, offset int, v uint64) int { offset -= sovProvider(v) base := offset @@ -2774,6 +2905,9 @@ func (m *Params) Size() (n int) { } l = m.ConsumerRewardDenomRegistrationFee.Size() n += 1 + l + sovProvider(uint64(l)) + if m.BlocksPerEpoch != 0 { + n += 1 + sovProvider(uint64(m.BlocksPerEpoch)) + } return n } @@ -3053,6 +3187,26 @@ func (m *ConsumerAddrsToPrune) Size() (n int) { return n } +func (m *ConsumerValidator) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ProviderConsAddr) + if l > 0 { + n += 1 + l + sovProvider(uint64(l)) + } + if m.Power != 0 { + n += 1 + sovProvider(uint64(m.Power)) + } + if m.ConsumerPublicKey != nil { + l = m.ConsumerPublicKey.Size() + n += 1 + l + sovProvider(uint64(l)) + } + return n +} + func sovProvider(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -4507,6 +4661,25 @@ func (m *Params) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlocksPerEpoch", wireType) + } + m.BlocksPerEpoch = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlocksPerEpoch |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) @@ -6327,6 +6500,145 @@ func (m *ConsumerAddrsToPrune) Unmarshal(dAtA []byte) error { } return nil } +func (m *ConsumerValidator) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConsumerValidator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsumerValidator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProviderConsAddr", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProviderConsAddr = append(m.ProviderConsAddr[:0], dAtA[iNdEx:postIndex]...) + if m.ProviderConsAddr == nil { + m.ProviderConsAddr = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Power", wireType) + } + m.Power = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Power |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerPublicKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsumerPublicKey == nil { + m.ConsumerPublicKey = &crypto.PublicKey{} + } + if err := m.ConsumerPublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProvider(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProvider + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipProvider(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0