Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
danwt opened this issue May 23, 2022 · 6 comments
Closed
Assignees
Labels
scope: testing Code review, testing, making sure the code is following the specification.

Comments

@danwt
Copy link
Contributor

danwt commented May 23, 2022

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.

@danwt danwt self-assigned this May 23, 2022
@danwt danwt moved this to Todo in Replicated Security May 23, 2022
@mpoke
Copy link
Contributor

mpoke commented May 30, 2022

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

There are two events of interest here:

  • Event A: A validator reaches maturity on the provider chain, which results in the validator’s status changing to unbonded if UnbondingOnHold == false. The check of maturity happens here, i.e., it checks if the unbonding time or height are <= than the current time or height.

  • Event B: A validator reaches maturity on all consumer chains, which results in either setting UnbondingOnHold to false (i.e., unlocking unbonding) or, if the validator already reached maturity (on the provider), changing the status to unbonded.

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:

  • Leaving the check for maturity in UnbondingOpCanComplete() as it is now and add a comment clarifying this issue. May lead to potential bugs in the future. (This is how we decided to go about it)

  • Change the check for maturity to include equality. This changes a bit the current behavior of Cosmos SDK. We need to make sure that between Event B (which unbonds a validator during DeliverTx) and Event A (which is the time when the SDK would have unbonded the validator) there are no other events that require the validator to be unbonding (e.g., Slash).

For the second option,

  • both Slash and Jail are called in BeginBlock, so moving the validator to unbonded during DeliverTx has no impact on them.

  • Delegate, Undelegate, and Redelegate seem to also not be affected, but this requires proper verification.

  • ApplyAndReturnValidatorSetUpdates() seems to not be impacted.

@mpoke
Copy link
Contributor

mpoke commented May 30, 2022

@danwt Do you agree?

@mpoke mpoke added the scope: testing Code review, testing, making sure the code is following the specification. label May 30, 2022
@danwt
Copy link
Contributor Author

danwt commented Jun 6, 2022

Yep, it makes sense!

@danwt danwt closed this as completed Jun 6, 2022
Repository owner moved this from Todo to Done in Replicated Security Jun 6, 2022
@mpoke mpoke reopened this Jun 30, 2022
Repository owner moved this from Done to In Progress in Replicated Security Jun 30, 2022
@mpoke mpoke moved this from In Progress to Todo in Replicated Security Jun 30, 2022
@mpoke
Copy link
Contributor

mpoke commented Jun 30, 2022

The logic in #111 (comment) may be broken since UnbondingOpCanComplete is called also by StopConsumerChain

@danwt
Copy link
Contributor Author

danwt commented Jul 4, 2022

As of

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

@mpoke mpoke assigned mpoke and unassigned danwt Jul 11, 2022
@mpoke mpoke moved this from Todo to Next in Replicated Security Jul 11, 2022
@mpoke mpoke moved this from Next to Waiting for review in Replicated Security Jul 12, 2022
@mpoke
Copy link
Contributor

mpoke commented Jul 12, 2022

Issue fixed by cosmos/cosmos-sdk#12537 cosmos/cosmos-sdk#12796

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope: testing Code review, testing, making sure the code is following the specification.
Projects
No open projects
Status: Done
Development

No branches or pull requests

2 participants