Unbonded validators prevent the TSS vote from passing
Description
Bonded validators can cast a vote to add a new TSS by sending a MsgCreateTSSVoter
. The issue is that there is a check to allow only bonded validators to vote, but for the vote to pass, the number of signers must be equal to the total number of validators (which includes unbonded/unbonding validators).
func (k msgServer) CreateTSSVoter(goCtx context.Context, msg *types.MsgCreateTSSVoter) (*types.MsgCreateTSSVoterResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
validators := k.StakingKeeper.GetAllValidators(ctx)
if !IsBondedValidator(msg.Creator, validators) {
return nil, sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, fmt.Sprintf("signer %s is not a bonded validator", msg.Creator))
}
// [ ... ]
// this needs full consensus on all validators.
if len(tssVoter.Signers) == len(validators) {
tss := types.TSS{
Creator: "",
Index: tssVoter.Chain,
Chain: tssVoter.Chain,
Address: tssVoter.Address,
Pubkey: tssVoter.Pubkey,
Signer: tssVoter.Signers,
FinalizedZetaHeight: uint64(ctx.BlockHeader().Height),
}
k.SetTSS(ctx, tss)
}
return &types.MsgCreateTSSVoterResponse{}, nil
}
Impact
If not every validator is a bonded validator, then it is impossible to add a new TSS as the vote can never pass. As anyone can become an unbonded validator, this would be easy to trigger and will likely happen in the course of normal operation as validators will unbond, putting them into an unbonding
state.
It is also possible for a bonded validator to sign the vote, become unbonded and removed, and have the vote still count.
Recommendations
The vote should only be passed when the set of currently bonded validators have all signed it.
Remediation
This issue has been acknowledged by ZetaChain.