cosmos / interchain-security

Interchain Security is an open sourced IBC application which allows cosmos blockchains to lease their proof-of-stake security to one another.
https://cosmos.github.io/interchain-security/
Other
156 stars 126 forks source link

Ensure definition of `IsMature` (cosmos-sdk/x/staking/types/validator.go) bug free #111

Closed danwt closed 2 years ago

danwt commented 2 years ago

In validatorUnbondingCanComplete the following code

func (k Keeper) validatorUnbondingCanComplete(ctx sdk.Context, id uint64) (found bool, err error) {
    val, found := k.GetValidatorByUnbondingId(ctx, id)
    if !found {
        return false, nil
    }

    if !val.IsMature(ctx.BlockTime(), ctx.BlockHeight()) {
        val.UnbondingOnHold = false
        k.SetValidator(ctx, val)
    } else {
        // If unbonding is mature complete it

https://github.com/cosmos/cosmos-sdk/blob/4cc285142ba73844e2c5929dedef59fa6db0f748/x/staking/keeper/unbonding.go#L326-L336

calls

// IsMature - is the current validator ready to unbond their coins
func (v Validator) IsMature(currentTime time.Time, currentHeight int64) bool {
    return v.UnbondingHeight < currentHeight && v.UnbondingTime.Before(currentTime)
}

https://github.com/cosmos/cosmos-sdk/blob/4cc285142ba73844e2c5929dedef59fa6db0f748/x/staking/types/validator.go#L180-L183

which has a different definition of maturity in UnbondAllMatureValidators

        if keyHeight <= blockHeight && (keyTime.Before(blockTime) || keyTime.Equal(blockTime)) {

https://github.com/cosmos/cosmos-sdk/blob/4cc285142ba73844e2c5929dedef59fa6db0f748/x/staking/keeper/validator.go#L420

and seems suspicious.

mpoke commented 2 years ago

Hi @danwt. This issue was already discussed on Slack. Copying here the conversation:

There are two events of interest here:

The edge case entails equality in the check for maturity in case of event B.

Event A always happens in the EndBlock of the Staking module.

Event B is triggered by an external module calling UnbondingOpCanComplete(). Currently, this is called only by the provider CCV module when receiving the last ACK from the consumer chains; this means that currently event B is triggered during DeliverTx (i.e., before EndBlock). Thus, in case of equality, setting UnbondingOnHold to false should do the trick since the validator will be unbonded in EndBlock(). However, if at some point in the future some external module will call UnbondingOpCanComplete() after the EndBlock of the staking module, then the validator will never unbond.

So, there are two options to go about it:

For the second option,

mpoke commented 2 years ago

@danwt Do you agree?

danwt commented 2 years ago

Yep, it makes sense!

mpoke commented 2 years ago

The logic in https://github.com/cosmos/interchain-security/issues/111#issuecomment-1140917312 may be broken since UnbondingOpCanComplete is called also by StopConsumerChain

danwt commented 2 years ago

As of

UnbondingCanComplete calls are deferred to EndBlock and provider.EndBlock occurs always after staking.Endblock

mpoke commented 2 years ago

Issue fixed by https://github.com/cosmos/cosmos-sdk/pull/12537 https://github.com/cosmos/cosmos-sdk/pull/12796