-
Notifications
You must be signed in to change notification settings - Fork 133
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP soft opt out code with incomplete boilerplate * proto changes * Seems like it should work * Unit test for UpdateLargestSoftOptOutValidatorPower * fixes and renames, unit tests work * update comment * log * Update proto/interchain_security/ccv/consumer/v1/consumer.proto Co-authored-by: Marius Poke <marius.poke@posteo.de> * better validation for soft opt out threshhold * improve test * slicestable * semantics and improved test * use correct key util * Update module.go * comment * updated semantics * separate files * fix TestMakeConsumerGenesis test * fix naming * change upper bound on soft opt out thresh * fix test * allow empty valset for tests * gofumpt and fix from merge * Update x/ccv/consumer/types/params_test.go * Update x/ccv/consumer/types/params.go * Soft opt out diff tests (#847) * wip * fixes for ts build * AI fixed my bug lol * throw error when needed * comment * disable soft opt-out in diff testing * update diff testing model * update UTs --------- Co-authored-by: mpoke <marius.poke@posteo.de> * add comment about beginblocker order requirement for soft opt-out --------- Co-authored-by: Jehan Tremback <hi@jehan.email> Co-authored-by: Marius Poke <marius.poke@posteo.de> Co-authored-by: Simon Noetzlin <simon.ntz@gmail.com>
- Loading branch information
1 parent
a718095
commit 1368b95
Showing
21 changed files
with
580 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package keeper | ||
|
||
import ( | ||
"encoding/binary" | ||
"sort" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/interchain-security/x/ccv/consumer/types" | ||
) | ||
|
||
// SetSmallestNonOptOutPower sets the smallest validator power that cannot soft opt out. | ||
func (k Keeper) SetSmallestNonOptOutPower(ctx sdk.Context, power uint64) { | ||
store := ctx.KVStore(k.storeKey) | ||
store.Set(types.SmallestNonOptOutPowerKey(), sdk.Uint64ToBigEndian(power)) | ||
} | ||
|
||
// UpdateSmallestNonOptOutPower updates the smallest validator power that cannot soft opt out. | ||
// This is the smallest validator power such that the sum of the power of all validators with a lower power | ||
// is less than [SoftOptOutThreshold] of the total power of all validators. | ||
func (k Keeper) UpdateSmallestNonOptOutPower(ctx sdk.Context) { | ||
// get soft opt-out threshold | ||
optOutThreshold := sdk.MustNewDecFromStr(k.GetSoftOptOutThreshold(ctx)) | ||
if optOutThreshold.IsZero() { | ||
// If the SoftOptOutThreshold is zero, then soft opt-out is disable. | ||
// Setting the smallest non-opt-out power to zero, fixes the diff-testing | ||
// when soft opt-out is disable. | ||
k.SetSmallestNonOptOutPower(ctx, uint64(0)) | ||
return | ||
} | ||
|
||
// get all validators | ||
valset := k.GetAllCCValidator(ctx) | ||
|
||
// Valset should only be empty for hacky tests. Log error in case this ever happens in prod. | ||
if len(valset) == 0 { | ||
k.Logger(ctx).Error("UpdateSoftOptOutThresholdPower called with empty validator set") | ||
return | ||
} | ||
|
||
// sort validators by power ascending | ||
sort.SliceStable(valset, func(i, j int) bool { | ||
return valset[i].Power < valset[j].Power | ||
}) | ||
|
||
// get total power in set | ||
totalPower := sdk.ZeroDec() | ||
for _, val := range valset { | ||
totalPower = totalPower.Add(sdk.NewDecFromInt(sdk.NewInt(val.Power))) | ||
} | ||
|
||
// get power of the smallest validator that cannot soft opt out | ||
powerSum := sdk.ZeroDec() | ||
for _, val := range valset { | ||
powerSum = powerSum.Add(sdk.NewDecFromInt(sdk.NewInt(val.Power))) | ||
// if powerSum / totalPower > SoftOptOutThreshold | ||
if powerSum.Quo(totalPower).GT(optOutThreshold) { | ||
// set smallest non opt out power | ||
k.SetSmallestNonOptOutPower(ctx, uint64(val.Power)) | ||
k.Logger(ctx).Info("smallest non opt out power updated", "power", val.Power) | ||
return | ||
} | ||
} | ||
panic("UpdateSoftOptOutThresholdPower should not reach this point. Incorrect logic!") | ||
} | ||
|
||
// GetSmallestNonOptOutPower returns the smallest validator power that cannot soft opt out. | ||
func (k Keeper) GetSmallestNonOptOutPower(ctx sdk.Context) int64 { | ||
store := ctx.KVStore(k.storeKey) | ||
bz := store.Get(types.SmallestNonOptOutPowerKey()) | ||
if bz == nil { | ||
return 0 | ||
} | ||
return int64(binary.BigEndian.Uint64(bz)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package keeper_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/cosmos/interchain-security/testutil/crypto" | ||
testkeeper "github.com/cosmos/interchain-security/testutil/keeper" | ||
"github.com/cosmos/interchain-security/x/ccv/consumer/types" | ||
"github.com/stretchr/testify/require" | ||
tmtypes "github.com/tendermint/tendermint/types" | ||
) | ||
|
||
// Tests that UpdateSmallestNonOptOutPower updates the smallest validator power that cannot soft opt out. | ||
// Soft opt out allows the bottom [SoftOptOutThreshold] portion of validators in the set to opt out. | ||
// UpdateSmallestNonOptOutPower should update the smallest validator power that cannot opt out. | ||
func TestUpdateSmallestNonOptOutPower(t *testing.T) { | ||
cIds := crypto.GenMultipleCryptoIds(7, 682934679238) | ||
|
||
testCases := []struct { | ||
name string | ||
// soft opt out threshold set as param | ||
optOutThresh string | ||
// validators to set in store | ||
validators []*tmtypes.Validator | ||
// expected smallest power of validator which cannot opt out | ||
expSmallestNonOptOutValPower int64 | ||
}{ | ||
{ | ||
name: "One", | ||
optOutThresh: "0.05", | ||
validators: []*tmtypes.Validator{ | ||
tmtypes.NewValidator(cIds[0].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[1].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[2].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[3].TMCryptoPubKey(), 3), | ||
tmtypes.NewValidator(cIds[4].TMCryptoPubKey(), 49), | ||
tmtypes.NewValidator(cIds[5].TMCryptoPubKey(), 51), | ||
}, | ||
// 107 total power, validator with 3 power passes 0.05 threshold (6 / 107 = 0.056) and cannot opt out | ||
expSmallestNonOptOutValPower: 3, | ||
}, | ||
{ | ||
name: "One in different order", | ||
optOutThresh: "0.05", | ||
validators: []*tmtypes.Validator{ | ||
tmtypes.NewValidator(cIds[0].TMCryptoPubKey(), 3), | ||
tmtypes.NewValidator(cIds[1].TMCryptoPubKey(), 51), | ||
tmtypes.NewValidator(cIds[2].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[3].TMCryptoPubKey(), 49), | ||
tmtypes.NewValidator(cIds[4].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[5].TMCryptoPubKey(), 1), | ||
}, | ||
// Same result as first test case, just confirms order of validators doesn't matter | ||
expSmallestNonOptOutValPower: 3, | ||
}, | ||
{ | ||
name: "Two", | ||
optOutThresh: "0.05", | ||
validators: []*tmtypes.Validator{ | ||
tmtypes.NewValidator(cIds[0].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[1].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[2].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[3].TMCryptoPubKey(), 3), | ||
tmtypes.NewValidator(cIds[4].TMCryptoPubKey(), 500), | ||
}, | ||
// 506 total power, validator with 500 passes 0.05 threshold and cannot opt out | ||
expSmallestNonOptOutValPower: 500, | ||
}, | ||
{ | ||
name: "Three", | ||
optOutThresh: "0.199999", | ||
validators: []*tmtypes.Validator{ | ||
tmtypes.NewValidator(cIds[0].TMCryptoPubKey(), 54), | ||
tmtypes.NewValidator(cIds[1].TMCryptoPubKey(), 53), | ||
tmtypes.NewValidator(cIds[2].TMCryptoPubKey(), 52), | ||
tmtypes.NewValidator(cIds[3].TMCryptoPubKey(), 51), | ||
tmtypes.NewValidator(cIds[4].TMCryptoPubKey(), 50), | ||
tmtypes.NewValidator(cIds[5].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[6].TMCryptoPubKey(), 1), | ||
}, | ||
// 262 total power, (50 + 1 + 1) / 262 ~= 0.19, validator with 51 passes 0.199999 threshold and cannot opt out | ||
expSmallestNonOptOutValPower: 51, | ||
}, | ||
{ | ||
name: "soft opt-out disabled", | ||
optOutThresh: "0", | ||
validators: []*tmtypes.Validator{ | ||
tmtypes.NewValidator(cIds[0].TMCryptoPubKey(), 54), | ||
tmtypes.NewValidator(cIds[1].TMCryptoPubKey(), 53), | ||
tmtypes.NewValidator(cIds[2].TMCryptoPubKey(), 52), | ||
tmtypes.NewValidator(cIds[3].TMCryptoPubKey(), 51), | ||
tmtypes.NewValidator(cIds[4].TMCryptoPubKey(), 50), | ||
tmtypes.NewValidator(cIds[5].TMCryptoPubKey(), 1), | ||
tmtypes.NewValidator(cIds[6].TMCryptoPubKey(), 1), | ||
}, | ||
expSmallestNonOptOutValPower: 0, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
keeperParams := testkeeper.NewInMemKeeperParams(t) | ||
// explicitly register codec with public key interface | ||
keeperParams.RegisterSdkCryptoCodecInterfaces() | ||
consumerKeeper, ctx, ctrl, _ := testkeeper.GetConsumerKeeperAndCtx(t, keeperParams) | ||
moduleParams := types.DefaultParams() | ||
|
||
moduleParams.SoftOptOutThreshold = tc.optOutThresh | ||
consumerKeeper.SetParams(ctx, moduleParams) | ||
defer ctrl.Finish() | ||
|
||
// set validators in store | ||
SetCCValidators(t, consumerKeeper, ctx, tc.validators) | ||
|
||
// update smallest power of validator which cannot opt out | ||
consumerKeeper.UpdateSmallestNonOptOutPower(ctx) | ||
|
||
// expect smallest power of validator which cannot opt out to be updated | ||
require.Equal(t, tc.expSmallestNonOptOutValPower, consumerKeeper.GetSmallestNonOptOutPower(ctx)) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.