Skip to content

Commit

Permalink
governance delegations invariant
Browse files Browse the repository at this point in the history
  • Loading branch information
giunatale committed Sep 24, 2024
1 parent 671755c commit 86031e6
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 5 deletions.
16 changes: 16 additions & 0 deletions x/gov/keeper/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ func (k Keeper) IterateGovernorValShares(ctx sdk.Context, governorAddr types.Gov
}
}

// IterateGovernorDelegations iterates over all governor delegations
func (k Keeper) IterateGovernorDelegations(ctx sdk.Context, governorAddr types.GovernorAddress, cb func(index int64, delegation v1.GovernanceDelegation) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.GovernanceDelegationsByGovernorKey(governorAddr, []byte{}))
defer iterator.Close()

for i := int64(0); iterator.Valid(); iterator.Next() {
var delegation v1.GovernanceDelegation
k.cdc.MustUnmarshal(iterator.Value(), &delegation)
if cb(i, delegation) {
break
}
i++
}
}

// GetGovernorValSharesByValidator gets all governor validator shares for a specific validator
func (k Keeper) GetGovernorValSharesByValidator(ctx sdk.Context, validatorAddr sdk.ValAddress) []v1.GovernorValShares {
store := ctx.KVStore(k.storeKey)
Expand Down
79 changes: 74 additions & 5 deletions x/gov/keeper/invariants.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

"github.com/atomone-hub/atomone/x/gov/types"
v1 "github.com/atomone-hub/atomone/x/gov/types/v1"
Expand All @@ -15,6 +16,7 @@ import (
func RegisterInvariants(ir sdk.InvariantRegistry, keeper *Keeper, bk types.BankKeeper) {
ir.RegisterRoute(types.ModuleName, "module-account", ModuleAccountInvariant(keeper, bk))
ir.RegisterRoute(types.ModuleName, "governors-voting-power", GovernorsVotingPowerInvariant(keeper, keeper.sk))
ir.RegisterRoute(types.ModuleName, "governors-delegations", GovernorsDelegationsInvariant(keeper, keeper.sk))
}

// AllInvariants runs all invariants of the governance module
Expand Down Expand Up @@ -69,14 +71,14 @@ func GovernorsVotingPowerInvariant(keeper *Keeper, sk types.StakingKeeper) sdk.I
keeper.IterateGovernorValShares(ctx, governor.GetAddress(), func(index int64, shares v1.GovernorValShares) bool {
validatorAddr, err := sdk.ValAddressFromBech32(shares.ValidatorAddress)
if err != nil {
invariantStr = sdk.FormatInvariant(types.ModuleName, "governor %s voting power",
invariantStr = sdk.FormatInvariant(types.ModuleName, fmt.Sprintf("governor %s voting power", governor.GetAddress().String()),
fmt.Sprintf("failed to parse validator address %s: %v", shares.ValidatorAddress, err))
fail = true
return true
}
validator, found := sk.GetValidator(ctx, validatorAddr)
if !found {
invariantStr = sdk.FormatInvariant(types.ModuleName, "governor %s voting power",
invariantStr = sdk.FormatInvariant(types.ModuleName, fmt.Sprintf("governor %s voting power", governor.GetAddress().String()),
fmt.Sprintf("validator %s not found", validatorAddr.String()))
fail = true
return true
Expand All @@ -95,10 +97,77 @@ func GovernorsVotingPowerInvariant(keeper *Keeper, sk types.StakingKeeper) sdk.I
return broken // break on first broken invariant
})
if !fail {
invariantStr = sdk.FormatInvariant(types.ModuleName, "governor %s voting power",
fmt.Sprintf("\texpected %s voting power: %s\n\tactual voting power: %s\n",
brokenGovernorAddr, expectedVotingPower, actualVotingPower))
invariantStr = sdk.FormatInvariant(types.ModuleName, fmt.Sprintf("governor %s voting power", brokenGovernorAddr),
fmt.Sprintf("\texpected voting power: %s\n\tactual voting power: %s\n", expectedVotingPower, actualVotingPower))
}
return invariantStr, broken
}
}

// GovernorsDelegationsInvariant checks that the validator shares resulting from all
// governor delegations actually correspond to the stored governor validator shares
func GovernorsDelegationsInvariant(keeper *Keeper, sk types.StakingKeeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
var (
broken bool = false

Check failure on line 112 in x/gov/keeper/invariants.go

View workflow job for this annotation

GitHub Actions / golangci-lint

ST1023: should omit type bool from declaration; it will be inferred from the right-hand side (stylecheck)

Check failure on line 112 in x/gov/keeper/invariants.go

View workflow job for this annotation

GitHub Actions / Analyze

ST1023: should omit type bool from declaration; it will be inferred from the right-hand side (stylecheck)
invariantStr string
)

keeper.IterateGovernors(ctx, func(index int64, governor v1.GovernorI) bool {
// check that if governor is active, it has a valid governance self-delegation
if governor.IsActive() {
if del, ok := keeper.GetGovernanceDelegation(ctx, sdk.AccAddress(governor.GetAddress())); !ok || !governor.GetAddress().Equals(types.MustGovernorAddressFromBech32(del.GovernorAddress)) {
invariantStr = sdk.FormatInvariant(types.ModuleName, fmt.Sprintf("governor %s delegations", governor.GetAddress().String()),
"active governor without governance self-delegation")
broken = true
return true
}
}

valShares := make(map[string]sdk.Dec)
keeper.IterateGovernorDelegations(ctx, governor.GetAddress(), func(index int64, delegation v1.GovernanceDelegation) bool {
delAddr := sdk.MustAccAddressFromBech32(delegation.DelegatorAddress)
keeper.sk.IterateDelegations(ctx, delAddr, func(_ int64, delegation stakingtypes.DelegationI) (stop bool) {
validatorAddr := delegation.GetValidatorAddr()
shares := delegation.GetShares()
if _, ok := valShares[validatorAddr.String()]; !ok {
valShares[validatorAddr.String()] = sdk.ZeroDec()
}
valShares[validatorAddr.String()] = valShares[validatorAddr.String()].Add(shares)
return false
})
return false
})

for valAddrStr, shares := range valShares {
validatorAddr, _ := sdk.ValAddressFromBech32(valAddrStr)
valShares, ok := keeper.GetGovernorValShares(ctx, governor.GetAddress(), validatorAddr)
if !ok {
invariantStr = sdk.FormatInvariant(types.ModuleName, fmt.Sprintf("governor %s delegations", governor.GetAddress().String()),
fmt.Sprintf("validator %s shares not found", valAddrStr))
broken = true
return true
}
if !valShares.Shares.Equal(shares) {
invariantStr = sdk.FormatInvariant(types.ModuleName, fmt.Sprintf("governor %s delegations", governor.GetAddress().String()),
fmt.Sprintf("stored shares %s for validator %s do not match actual shares %s", valShares.Shares, valAddrStr, shares))
broken = true
return true
}
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism

keeper.IterateGovernorValShares(ctx, governor.GetAddress(), func(index int64, shares v1.GovernorValShares) bool {
if _, ok := valShares[shares.ValidatorAddress]; !ok && shares.Shares.GT(sdk.ZeroDec()) {
invariantStr = sdk.FormatInvariant(types.ModuleName, fmt.Sprintf("governor %s delegations", governor.GetAddress().String()),
fmt.Sprintf("non-zero (%s) shares stored for validator %s where there should be none", shares.Shares, shares.ValidatorAddress))
broken = true
return true
}
return false
})

return broken
})
return invariantStr, broken
}
}

0 comments on commit 86031e6

Please sign in to comment.