fix(fsm): guard against empty PaymentPercents slice in HandleCertificateResults#426
Open
amathxbt wants to merge 2 commits into
Open
fix(fsm): guard against empty PaymentPercents slice in HandleCertificateResults#426amathxbt wants to merge 2 commits into
amathxbt wants to merge 2 commits into
Conversation
andrewnguyen22
requested changes
Jun 27, 2026
Collaborator
There was a problem hiding this comment.
This part is real:
HandleCertificateResults()only checksRewardRecipients != nilatfsm/automatic.go:152. An empty PaymentPercents slice can pass into
UpsertCommitteeData()atfsm/automatic.go:224.
But this does not “silently burn all block rewards on every block” immediately. DistributeCommitteeRewards() explicitly skips empty PaymentPercents at fsm/committee.go:142.
The more accurate risk is malformed/empty reward samples can be persisted, which can distort later reward accounting or cause later under-distribution/burn when non-empty samples are eventually distributed.
| return lib.ErrNilRewardRecipients() | ||
| } | ||
| // guard against an empty PaymentPercents slice causing a silent no-op reward distribution | ||
| if len(qc.Results.RewardRecipients.PaymentPercents) == 0 && qc.Results.RewardRecipients != nil { |
Collaborator
There was a problem hiding this comment.
Use the existing shared validation instead of an ad hoc check:
if err := qc.Results.RewardRecipients.CheckBasic(); err != nil {
return err
}
…heck The previous commit added a guard: if len(...PaymentPercents) == 0 && qc.Results.RewardRecipients != nil The second condition is dead code — RewardRecipients non-nil is already enforced two guards above. Simplify to just the length check. Also update the comment to accurately describe the risk: an empty slice would be persisted by UpsertCommitteeData and silently skipped by DistributeCommitteeRewards, causing NumberOfSamples to accumulate without corresponding recipients and distorting later reward accounting.
Author
|
Thanks @andrewnguyen22 you're right that the immediate-burn description was overstated. Updated the branch with:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
In
fsm/automatic.go,HandleCertificateResults()checks thatRewardRecipientsis non-nil but does not guard againstPaymentPercentsbeing an empty slice:An empty
PaymentPercentsslice causes the reward-distribution loop to silently skip all payments — validators complete work but receive no rewards, and the block is accepted without error. A malicious or buggy proposer can craft aCertificateResultwith a non-nilRewardRecipientsbut zeroPaymentPercentsto silently starve the committee of rewards for that block height.Fix
Return
ErrInvalidPercentAllocationwhenPaymentPercentsis empty after the existing nil guard, making the invariant explicit and ensuring the block is rejected rather than silently misprocessed.Impact
Critical — malicious proposer can craft blocks that pass validation but deliver zero rewards to the committee.