Skip to content

Commit

Permalink
feat!: compute partial sets (#1702)
Browse files Browse the repository at this point in the history
* init commit

* nit change

* cleaning up

* clean up

* fix distribution test

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

Co-authored-by: Simon Noetzlin <simon.ntz@gmail.com>

* took into Simon's comments

* took into rest of the comments

* nit change

* return an error if validator cannot opt out from a Top N chain

* removed automatic opt-in for validators that vote Yes on proposals

* tiny fix for E2E tests

* nit change to remove unecessary else

* fixed topN == 0 issue

---------

Co-authored-by: Simon Noetzlin <simon.ntz@gmail.com>
  • Loading branch information
insumity and sainoe authored Mar 25, 2024
1 parent 79abc14 commit b251141
Show file tree
Hide file tree
Showing 22 changed files with 727 additions and 1,106 deletions.
13 changes: 0 additions & 13 deletions proto/interchain_security/ccv/provider/v1/provider.proto
Original file line number Diff line number Diff line change
Expand Up @@ -326,16 +326,3 @@ message ConsumerRewardsAllocation {
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins"
];
}

// OptedInValidator is used to store a opted-in validator
// to a consumer chain with the following mapping: (chainID, providerAddr) -> optedInValidator
message OptedInValidator {
// validator address
bytes provider_addr = 1;
// block height at which the validator opted-in
int64 block_height = 2;
// validator voting power at the block it opted-in
int64 power = 3;
// public key used by the validator on the consumer
bytes public_key = 4;
}
1 change: 1 addition & 0 deletions tests/e2e/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ func (tr TestConfig) submitConsumerAdditionProposal(
UnbondingPeriod: params.UnbondingPeriod,
Deposit: fmt.Sprint(action.Deposit) + `stake`,
DistributionTransmissionChannel: action.DistributionChannel,
TopN: 100,
}

bz, err := json.Marshal(prop)
Expand Down
3 changes: 2 additions & 1 deletion tests/mbt/driver/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,8 @@ func (s *Driver) ConfigureNewPath(consumerChain, providerChain *ibctesting.TestC
stakingValidators = append(stakingValidators, v)
}

nextValidators := s.providerKeeper().ComputeNextEpochConsumerValSet(s.providerCtx(), string(consumerChainId), stakingValidators)
considerAll := func(validator stakingtypes.Validator) bool { return true }
nextValidators := s.providerKeeper().ComputeNextEpochConsumerValSet(s.providerCtx(), string(consumerChainId), stakingValidators, considerAll)
s.providerKeeper().SetConsumerValSet(s.providerCtx(), string(consumerChainId), nextValidators)

err = s.providerKeeper().SetConsumerGenesis(
Expand Down
29 changes: 0 additions & 29 deletions testutil/keeper/mocks.go

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

56 changes: 0 additions & 56 deletions x/ccv/provider/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package keeper
import (
"fmt"

"cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkgov "github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
Expand Down Expand Up @@ -177,61 +175,7 @@ func (h Hooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64
func (h Hooks) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) {
}

// AfterProposalVote opts in validators that vote YES (with 100% weight) on a `ConsumerAdditionProposal`. If a
// validator votes multiple times, only the last vote would be considered on whether the validator is opted in or not.
func (h Hooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) {
validator, found := h.k.stakingKeeper.GetValidator(ctx, voterAddr.Bytes())
if !found {
return
}

consAddr, err := validator.GetConsAddr()
if err != nil {
h.k.Logger(ctx).Error("could not extract validator's consensus address",
"error", err.Error(),
"validator acc addr", voterAddr,
)
return
}

chainID, found := h.k.GetProposedConsumerChain(ctx, proposalID)
if !found {
return
}

vote, found := h.k.govKeeper.GetVote(ctx, proposalID, voterAddr)
if !found {
h.k.Logger(ctx).Error("could not find vote for validator",
"validator acc addr", voterAddr,
"proposalID", proposalID,
)
return
}

if len(vote.Options) == 1 && vote.Options[0].Option == v1.VoteOption_VOTE_OPTION_YES {
// only consider votes that vote YES with 100% weight
weight, err := sdk.NewDecFromStr(vote.Options[0].Weight)
if err != nil {
h.k.Logger(ctx).Error("could not extract decimal value from vote's weight",
"vote", vote,
"error", err.Error(),
)
return
}
if !weight.Equal(math.LegacyNewDec(1)) {
h.k.Logger(ctx).Error("single vote does not have a weight of 1",
"vote", vote,
)
return
}

// in the validator is already to-be-opted in, the `SetToBeOptedIn` is a no-op
h.k.SetToBeOptedIn(ctx, chainID, providertypes.NewProviderConsAddress(consAddr))
} else {
// if vote is not a YES vote with 100% weight, we delete the validator from to-be-opted in
// if the validator is not to-be-opted in, the `DeleteToBeOptedIn` is a no-op
h.k.DeleteToBeOptedIn(ctx, chainID, providertypes.NewProviderConsAddress(consAddr))
}
}

func (h Hooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) {
Expand Down
89 changes: 0 additions & 89 deletions x/ccv/provider/keeper/hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,14 @@ import (

"cosmossdk.io/math"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
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"
providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper"
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
)

func TestValidatorConsensusKeyInUse(t *testing.T) {
Expand Down Expand Up @@ -229,87 +224,3 @@ func TestGetConsumerAdditionLegacyPropFromProp(t *testing.T) {
})
}
}

func TestAfterProposalVoteWithYesVote(t *testing.T) {
k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t))
defer ctrl.Finish()

providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey()
pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey)
providerAddr := types.NewProviderConsAddress(providerConsPubKey.Address().Bytes())

options := []*v1.WeightedVoteOption{{Option: v1.OptionYes, Weight: "1"}}
k.SetProposedConsumerChain(ctx, "chainID", 1)

gomock.InOrder(
mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(
stakingtypes.Validator{ConsensusPubkey: pkAny}, true),
mocks.MockGovKeeper.EXPECT().GetVote(ctx, gomock.Any(), gomock.Any()).Return(
v1.Vote{ProposalId: 1, Voter: "voter", Options: options}, true,
),
)

require.False(t, k.IsToBeOptedIn(ctx, "chainID", providerAddr))
k.Hooks().AfterProposalVote(ctx, 1, sdk.AccAddress{})
require.True(t, k.IsToBeOptedIn(ctx, "chainID", providerAddr))
}

func TestAfterProposalVoteWithNoVote(t *testing.T) {
testCases := []struct {
name string
options []*v1.WeightedVoteOption
setup func(sdk.Context, []*v1.WeightedVoteOption, testkeeper.MockedKeepers, *codectypes.Any)
}{
{
"Weighted vote with 100% NO",
[]*v1.WeightedVoteOption{{Option: v1.OptionNo, Weight: "1"}},
func(ctx sdk.Context, options []*v1.WeightedVoteOption,
mocks testkeeper.MockedKeepers, pubKey *codectypes.Any,
) {
gomock.InOrder(
mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(
stakingtypes.Validator{ConsensusPubkey: pubKey}, true),
mocks.MockGovKeeper.EXPECT().GetVote(ctx, gomock.Any(), gomock.Any()).Return(
v1.Vote{ProposalId: 1, Voter: "voter", Options: options}, true,
),
)
},
},
{
"Weighted vote with 99.9% YES and 0.1% NO",
[]*v1.WeightedVoteOption{{Option: v1.OptionYes, Weight: "0.999"}, {Option: v1.OptionNo, Weight: "0.001"}},
func(ctx sdk.Context, options []*v1.WeightedVoteOption,
mocks testkeeper.MockedKeepers, pubKey *codectypes.Any,
) {
gomock.InOrder(
mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return(
stakingtypes.Validator{ConsensusPubkey: pubKey}, true),
mocks.MockGovKeeper.EXPECT().GetVote(ctx, gomock.Any(), gomock.Any()).Return(
v1.Vote{ProposalId: 1, Voter: "voter", Options: options}, true,
),
)
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t))
defer ctrl.Finish()

providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey()
pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey)
providerAddr := types.NewProviderConsAddress(providerConsPubKey.Address().Bytes())

k.SetProposedConsumerChain(ctx, "chainID", 1)

tc.setup(ctx, tc.options, mocks, pkAny)

// set the validator to-be-opted in first to assert that a NO vote removes the validator from to-be-opted in
k.SetToBeOptedIn(ctx, "chainID", providerAddr)
require.True(t, k.IsToBeOptedIn(ctx, "chainID", providerAddr))
k.Hooks().AfterProposalVote(ctx, 1, sdk.AccAddress{})
require.False(t, k.IsToBeOptedIn(ctx, "chainID", providerAddr))
})
}
}
Loading

0 comments on commit b251141

Please sign in to comment.