Skip to content

Commit

Permalink
feat: extend consumer_validators query to return consumer valset be…
Browse files Browse the repository at this point in the history
…fore launch (#2164)

* (partially) renamed chain ids to consumer ids

* renamed proposal messages

* removed global slash entry

* fixed unit tests

* added new messages

* introduced new state

* added functionality for the register and initialize messages

* renamed (partially) chainIds to consumerIds

* set consumerId to chainId association during registration

* added extra check in the initialization so unknokwn, launched, or stopped chains cannot re-initialize

* added initial work on traversing initialized chains that are to-be-launched

* fixed rebase issues after bringing the VSCMaturedPackets work in

* made it so we traverse initialization records instead of addition proposals (+ additional changes so the unit tests pass)

* renamed more chainIDs to consumerIds

* removed ClientIdToChainId state because chainId already resides on the registration record

* nit fixes in go docs

* removed MsgConsumerAddition

* added CLI commands for new messages

* removed consumer modification proposal

* removed (partially) consumer removal proposal

* rebased to pick up the inactive-validators work (PR #2079)

* introduced consumerId in the equivocation messages (and a useful query for Hermes to get the consumerId)

* added safeguard so that a validator cannot opt-in to two different chains with the same chain id

* renamed some chainIDs to consumerIds

* updated based on comments

Co-authored-by: bernd-m <[email protected]>

* fixed integration tests

* rebased to pick up the removal of legacy proposals (#2130) and re-introduced old messages so that existing proposals can deserialize

* changes messages to only have MsgCreateConsumer and MsgUpdateConsumer and modified protos so that we are backward-compatible

* cleaned up slightly a few things (mostly committing & pushing) so people can pick up the latest changes

* fixed the CreateConsumer and UpdateConsumer logic and made most of the fields optional

* fixed hooks and the code around proposalId to consumerId

* pre-spawn query

* rebase

* nits

* feat: extend consumer validator query to return commission rate (backport #2162) (#2165)

* adapt #2162 changes for permissionless ICS

* nits

---------

Co-authored-by: kirdatatjana <[email protected]>

* remove panics

* renamed some chainIds to consumerIds

* took into account comments and also added safeguard to reject new proposals that still use deprecated messages (e.g., MsgConsumerAddition, etc.)

* Update x/ccv/provider/types/msg.go

Co-authored-by: bernd-m <[email protected]>

* removed double-gas charge on MsgCreateConsumer and imroved the logic of MsgUpdateConsumer

* added PopulateMinimumPowerInTopN tested

* took into account comments (using protos for marshalling string slice, fixed issues in the UpdateConsumer logic, added extra check to abort spurious proposals)

* update logic

* took into account comments (using protos for marshalling string slice, fixed issues in the UpdateConsumer logic, added extra check to abort spurious proposals)

* feat: add fields to consumer validators query (#2167)

* extend consumer validators query

* nit

* nits

* fix msg order

* deprecate power for consumer_power

* nits

* feat: first iteration on Permissionless ICS (#2117)

* (partially) renamed chain ids to consumer ids

* renamed proposal messages

* removed global slash entry

* fixed unit tests

* added new messages

* introduced new state

* added functionality for the register and initialize messages

* renamed (partially) chainIds to consumerIds

* set consumerId to chainId association during registration

* added extra check in the initialization so unknokwn, launched, or stopped chains cannot re-initialize

* added initial work on traversing initialized chains that are to-be-launched

* fixed rebase issues after bringing the VSCMaturedPackets work in

* made it so we traverse initialization records instead of addition proposals (+ additional changes so the unit tests pass)

* renamed more chainIDs to consumerIds

* removed ClientIdToChainId state because chainId already resides on the registration record

* nit fixes in go docs

* removed MsgConsumerAddition

* added CLI commands for new messages

* removed consumer modification proposal

* removed (partially) consumer removal proposal

* rebased to pick up the inactive-validators work (PR #2079)

* introduced consumerId in the equivocation messages (and a useful query for Hermes to get the consumerId)

* added safeguard so that a validator cannot opt-in to two different chains with the same chain id

* renamed some chainIDs to consumerIds

* updated based on comments

Co-authored-by: bernd-m <[email protected]>

* fixed integration tests

* rebased to pick up the removal of legacy proposals (#2130) and re-introduced old messages so that existing proposals can deserialize

* changes messages to only have MsgCreateConsumer and MsgUpdateConsumer and modified protos so that we are backward-compatible

* cleaned up slightly a few things (mostly committing & pushing) so people can pick up the latest changes

* fixed the CreateConsumer and UpdateConsumer logic and made most of the fields optional

* fixed hooks and the code around proposalId to consumerId

* feat: extend consumer validator query to return commission rate (backport #2162) (#2165)

* adapt #2162 changes for permissionless ICS

* nits

---------

Co-authored-by: kirdatatjana <[email protected]>

* renamed some chainIds to consumerIds

* took into account comments and also added safeguard to reject new proposals that still use deprecated messages (e.g., MsgConsumerAddition, etc.)

* Update x/ccv/provider/types/msg.go

Co-authored-by: bernd-m <[email protected]>

* removed double-gas charge on MsgCreateConsumer and imroved the logic of MsgUpdateConsumer

* added PopulateMinimumPowerInTopN tested

* took into account comments (using protos for marshalling string slice, fixed issues in the UpdateConsumer logic, added extra check to abort spurious proposals)

* feat: add fields to consumer validators query (#2167)

* extend consumer validators query

* nit

* nits

* fix msg order

* deprecate power for consumer_power

* modified the way we verify the new owner address, as well as nit refactoring on the ConsumerIds

* fixed some rebase issues and changed a proto to be backward-compatible

---------

Co-authored-by: bernd-m <[email protected]>
Co-authored-by: Simon Noetzlin <[email protected]>
Co-authored-by: kirdatatjana <[email protected]>

* cover stopped phase case

* fixed bug on removing previous spawn time & added tests

* added some additional tests

* added tests on the hooks

* removed check that spawn time is in the future

* feat: refactor consumer validator set computation (#2175)

* add UT

* nits

* address comments

* Update x/ccv/provider/keeper/partial_set_security.go

Co-authored-by: insumity <[email protected]>

* fix tests

---------

Co-authored-by: insumity <[email protected]>

* nit

* nits

* nit

---------

Co-authored-by: insumity <[email protected]>
Co-authored-by: bernd-m <[email protected]>
Co-authored-by: kirdatatjana <[email protected]>
  • Loading branch information
4 people committed Aug 28, 2024
1 parent 50475e6 commit 3ea2923
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 25 deletions.
53 changes: 46 additions & 7 deletions x/ccv/provider/keeper/grpc_query.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package keeper

import (
"bytes"
"context"
"fmt"
"sort"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -333,18 +335,55 @@ func (k Keeper) QueryConsumerValidators(goCtx context.Context, req *types.QueryC

ctx := sdk.UnwrapSDKContext(goCtx)

if _, found := k.GetConsumerClientId(ctx, consumerId); !found {
// chain has to have started; consumer client id is set for a chain during the chain's spawn time
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("no started consumer chain: %s", consumerId))
// get the consumer phase
phase := k.GetConsumerPhase(ctx, consumerId)
if phase == types.ConsumerPhase_CONSUMER_PHASE_UNSPECIFIED {
return nil, status.Errorf(codes.InvalidArgument, "cannot find a phase for consumer: %s", consumerId)
}

var validators []*types.QueryConsumerValidatorsValidator
// query consumer validator set

consumerValSet, err := k.GetConsumerValSet(ctx, consumerId)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
var consumerValSet []types.ConsensusValidator
var err error

// if the consumer launched, the consumer valset has been persisted
if phase == types.ConsumerPhase_CONSUMER_PHASE_LAUNCHED {
consumerValSet, err = k.GetConsumerValSet(ctx, consumerId)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
// if the consumer hasn't been launched or stopped, compute the consumer validator set
} else if phase != types.ConsumerPhase_CONSUMER_PHASE_STOPPED {
bondedValidators, err := k.GetLastBondedValidators(ctx)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to get last validators: %s", err))
}
minPower := int64(0)
// for TopN chains, compute the minPower that will be automatically opted in
if topN := k.GetTopN(ctx, consumerId); topN > 0 {
activeValidators, err := k.GetLastProviderConsensusActiveValidators(ctx)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to get active validators: %s", err))
}

minPower, err = k.ComputeMinPowerInTopN(ctx, activeValidators, topN)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to compute min power to opt in for chain %s: %s", consumerId, err))
}
}

consumerValSet = k.ComputeNextValidators(ctx, consumerId, bondedValidators, minPower)

// sort the address of the validators by ascending lexical order as they were persisted to the store
sort.Slice(consumerValSet, func(i, j int) bool {
return bytes.Compare(
consumerValSet[i].ProviderConsAddr,
consumerValSet[j].ProviderConsAddr,
) == -1
})
}

var validators []*types.QueryConsumerValidatorsValidator
for _, consumerVal := range consumerValSet {
provAddr := types.ProviderConsAddress{Address: consumerVal.ProviderConsAddr}
consAddr := provAddr.ToSdkConsAddr()
Expand Down
135 changes: 119 additions & 16 deletions x/ccv/provider/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package keeper_test

import (
"bytes"
"fmt"
"sort"
"testing"

"github.com/golang/mock/gomock"
Expand Down Expand Up @@ -101,41 +103,84 @@ func TestQueryConsumerValidators(t *testing.T) {
defer ctrl.Finish()

consumerId := "0"

req := types.QueryConsumerValidatorsRequest{
ConsumerId: consumerId,
}

// error returned from not-started chain
// error returned from not-existing chain
_, err := pk.QueryConsumerValidators(ctx, &req)
require.Error(t, err)

// set the consumer to the "registered" phase
pk.SetConsumerPhase(ctx, consumerId, types.ConsumerPhase_CONSUMER_PHASE_REGISTERED)

// expect empty valset
testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, 1) // -1 to allow the calls "AnyTimes"
res, err := pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Len(t, res.Validators, 0)

// create bonded validators
val1 := createStakingValidator(ctx, mocks, 1, 1, 1)
pk1, _ := val1.CmtConsPublicKey()
valConsAddr1, _ := val1.GetConsAddr()
providerAddr1 := types.NewProviderConsAddress(valConsAddr1)
pk1, _ := val1.CmtConsPublicKey()
consumerValidator1 := types.ConsensusValidator{ProviderConsAddr: providerAddr1.ToSdkConsAddr(), Power: 1, PublicKey: &pk1}
val1.Tokens = sdk.TokensFromConsensusPower(1, sdk.DefaultPowerReduction)
val1.Description = stakingtypes.Description{Moniker: "ConsumerValidator1"}
val1.Commission.Rate = math.LegacyMustNewDecFromStr("0.123")

val2 := createStakingValidator(ctx, mocks, 1, 2, 2)
pk2, _ := val2.CmtConsPublicKey()
valConsAddr2, _ := val2.GetConsAddr()
providerAddr2 := types.NewProviderConsAddress(valConsAddr2)
pk2, _ := val2.CmtConsPublicKey()
consumerValidator2 := types.ConsensusValidator{ProviderConsAddr: providerAddr2.ToSdkConsAddr(), Power: 2, PublicKey: &pk2}
val2.Tokens = sdk.TokensFromConsensusPower(2, sdk.DefaultPowerReduction)
val2.Description = stakingtypes.Description{Moniker: "ConsumerValidator2"}
val2.Commission.Rate = math.LegacyMustNewDecFromStr("0.123")
val2.Commission.Rate = math.LegacyMustNewDecFromStr("0.456")

val3 := createStakingValidator(ctx, mocks, 1, 3, 3)
pk3, _ := val3.CmtConsPublicKey()
valConsAddr3, _ := val3.GetConsAddr()
providerAddr3 := types.NewProviderConsAddress(valConsAddr3)
consumerValidator3 := types.ConsensusValidator{ProviderConsAddr: providerAddr3.ToSdkConsAddr(), Power: 3, PublicKey: &pk3}
val3.Tokens = sdk.TokensFromConsensusPower(3, sdk.DefaultPowerReduction)
val3.Description = stakingtypes.Description{Moniker: "ConsumerValidator3"}

mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr1).Return(val1, nil).AnyTimes()
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr2).Return(val2, nil).AnyTimes()
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr3).Return(val3, nil).AnyTimes()
mocks.MockStakingKeeper.EXPECT().PowerReduction(ctx).Return(sdk.DefaultPowerReduction).AnyTimes()
testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 2, []stakingtypes.Validator{val1, val2}, -1) // -1 to allow the calls "AnyTimes"

// set max provider consensus vals to include all validators
params := pk.GetParams(ctx)
params.MaxProviderConsensusValidators = 3
pk.SetParams(ctx, params)

// expect no validator to be returned since the consumer is Opt-In
res, err = pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Len(t, res.Validators, 0)

// opt in one validator
pk.SetOptedIn(ctx, consumerId, providerAddr1)

// set up the client id so the chain looks like it "started"
pk.SetConsumerClientId(ctx, consumerId, "clientID")
pk.SetConsumerValSet(ctx, consumerId, []types.ConsensusValidator{consumerValidator1, consumerValidator2})
// set a consumer commission rate for val1
val1ConsComRate := math.LegacyMustNewDecFromStr("0.456")
val1ConsComRate := math.LegacyMustNewDecFromStr("0.789")
pk.SetConsumerCommissionRate(ctx, consumerId, providerAddr1, val1ConsComRate)

expectedResponse := types.QueryConsumerValidatorsResponse{
// expect opted-in validator
res, err = pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Len(t, res.Validators, 1)
require.Equal(t, res.Validators[0].ProviderAddress, providerAddr1.String())

// update consumer TopN param
pk.SetConsumerPowerShapingParameters(ctx, consumerId, types.PowerShapingParameters{Top_N: 50})

// expect both opted-in and topN validator
expRes := types.QueryConsumerValidatorsResponse{
Validators: []*types.QueryConsumerValidatorsValidator{
{
ProviderAddress: providerAddr1.String(),
Expand Down Expand Up @@ -168,22 +213,80 @@ func TestQueryConsumerValidators(t *testing.T) {
},
}

mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr1).Return(val1, nil).AnyTimes()
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr2).Return(val2, nil).AnyTimes()
mocks.MockStakingKeeper.EXPECT().PowerReduction(ctx).Return(sdk.DefaultPowerReduction).AnyTimes()
// sort the address of the validators by ascending lexical order as they were persisted to the store
sort.Slice(expRes.Validators, func(i, j int) bool {
return bytes.Compare(
expRes.Validators[i].ConsumerKey.GetEd25519(),
expRes.Validators[j].ConsumerKey.GetEd25519(),
) == -1
})

testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 2, []stakingtypes.Validator{val1, val2}, -1) // -1 to allow the calls "AnyTimes"
res, err = pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Equal(t, &expRes, res)

res, err := pk.QueryConsumerValidators(ctx, &req)
// expect same result when consumer is in "initialized" phase
pk.SetConsumerPhase(ctx, consumerId, types.ConsumerPhase_CONSUMER_PHASE_INITIALIZED)
res, err = pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Equal(t, &expectedResponse, res)
require.Equal(t, &expRes, res)

// set consumer to the "launched" phase
pk.SetConsumerPhase(ctx, consumerId, types.ConsumerPhase_CONSUMER_PHASE_LAUNCHED)

// expect an empty consumer valset
// since neither QueueVSCPackets or MakeConsumerGenesis was called at this point
res, err = pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Empty(t, res)

// set consumer valset
pk.SetConsumerValSet(ctx, consumerId, []types.ConsensusValidator{
consumerValidator1,
consumerValidator2,
consumerValidator3,
})

expRes.Validators = append(expRes.Validators, &types.QueryConsumerValidatorsValidator{
ProviderAddress: providerAddr3.String(),
ConsumerKey: &pk3,
ConsumerPower: 3,
ConsumerCommissionRate: val3.Commission.Rate,
Description: val3.Description,
ProviderOperatorAddress: val3.OperatorAddress,
Jailed: val3.Jailed,
Status: val3.Status,
ProviderTokens: val3.Tokens,
ProviderCommissionRate: val3.Commission.Rate,
ProviderPower: 3,
ValidatesCurrentEpoch: true,
})

// sort the address of the validators by ascending lexical order as they were persisted to the store
sort.Slice(expRes.Validators, func(i, j int) bool {
return bytes.Compare(
expRes.Validators[i].ConsumerKey.GetEd25519(),
expRes.Validators[j].ConsumerKey.GetEd25519(),
) == -1
})

res, err = pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Equal(t, &expRes, res)

// validator with no set consumer commission rate
pk.DeleteConsumerCommissionRate(ctx, consumerId, providerAddr1)
// because no consumer commission rate is set, the validator's set commission rate on the provider is used
res, err = pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Equal(t, val1.Commission.Rate, res.Validators[0].ConsumerCommissionRate)

// set consumer to stopped phase
pk.SetConsumerPhase(ctx, consumerId, types.ConsumerPhase_CONSUMER_PHASE_STOPPED)
// expect empty valset
res, err = pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Empty(t, res)
}

func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion x/ccv/provider/types/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package types
import (
"encoding/json"
"fmt"
cmttypes "github.com/cometbft/cometbft/types"
"strconv"
"strings"
"time"

cmttypes "github.com/cometbft/cometbft/types"

ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint"

errorsmod "cosmossdk.io/errors"
Expand Down
2 changes: 1 addition & 1 deletion x/ccv/provider/types/provider.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3ea2923

Please sign in to comment.