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

[Bug]: ValidateVoteExtensions Voting Power Mismatch #17517

Closed
davidterpay opened this issue Aug 23, 2023 · 1 comment · Fixed by #17518
Closed

[Bug]: ValidateVoteExtensions Voting Power Mismatch #17517

davidterpay opened this issue Aug 23, 2023 · 1 comment · Fixed by #17518
Labels

Comments

@davidterpay
Copy link
Contributor

davidterpay commented Aug 23, 2023

What happened?

ValidateVoteExtensions verifies vote extensions by querying the current voting power (i.e. bonded tokens) that the validator has. If the voting power across on all of the vote extensions exceeds the 2/3+ threshold it is considered valid.

Although the latter part of the business logic is sound, utilizing the staking keeper to get the voting power creates possible liveness issues because the voting power on the vote extensions themselves are propagated from Comet/TM which may be outdated since the validator set updates are not propagated to comet for a few blocks.

The solution here is to utilize the voting power provided on the vote extensions themselves to verify the whether the total set of vote extensions met the threshold.

valConsAddr := sdk.ConsAddress(vote.Validator.Address)
bondedTokens, cmtPubKeyProto, err := valStore.BondedTokensAndPubKeyByConsAddr(ctx, valConsAddr)
if err != nil {
return fmt.Errorf("failed to get validator %X info (bonded tokens and public key): %w", valConsAddr, err)
}
cmtPubKey, err := cryptoenc.PubKeyFromProto(cmtPubKeyProto)
if err != nil {
return fmt.Errorf("failed to convert validator %X public key: %w", valConsAddr, err)
}
cve := cmtproto.CanonicalVoteExtension{
Extension: vote.VoteExtension,
Height: currentHeight - 1, // the vote extension was signed in the previous height
Round: int64(extCommit.Round),
ChainId: chainID,
}
extSignBytes, err := marshalDelimitedFn(&cve)
if err != nil {
return fmt.Errorf("failed to encode CanonicalVoteExtension: %w", err)
}
if !cmtPubKey.VerifySignature(extSignBytes, vote.ExtensionSignature) {
return fmt.Errorf("failed to verify validator %X vote extension signature", valConsAddr)
}
sumVP = sumVP.Add(bondedTokens)
}

Cosmos SDK Version

v0.50.0-rc.0

How to reproduce?

Say there are two validators in the network, val1 and val2:

Block H - 1 or H -2:

  • Val1 has a voting power of 80
  • Val2 has a voting power of 20

Only val1 signs off on block H - 1 which constitutes a valid block (> 2/3+). In block H-1, there is a redelegation transaction that moves 30 voting power from val1 to val2.

Block H:

  • Val1 has voting power of 50 (from the perspective of the application)
  • Val2 has a voting power of 50 (from the perspective of the application)

When validator vote extensions is called in prepare/process proposal, it will see that Val1 only has a voting power of 50 and will reject the set of vote extensions (even though it should still be 80 since this is what comet is utilizing).

from Skip wit <3

@davidterpay
Copy link
Contributor Author

#17518 linked the PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant