Skip to content

fix(bft): stake-weighted consensus threshold and vote counting - #502

Merged
RUKAYAT-CODER merged 2 commits into
rinafcode:mainfrom
zakariyaufarida5-wq:fix/496-stake-weighted-bft-consensus
Aug 17, 2026
Merged

fix(bft): stake-weighted consensus threshold and vote counting#502
RUKAYAT-CODER merged 2 commits into
rinafcode:mainfrom
zakariyaufarida5-wq:fix/496-stake-weighted-bft-consensus

Conversation

@zakariyaufarida5-wq

Copy link
Copy Markdown
Contributor

Closes #496

Summary

bft_consensus.rs::update_consensus_state computed the Byzantine threshold purely from the count of active validators (floor(2n/3)+1), even though it already tracks each validator's stake. That let an attacker cheapen a Sybil attack by registering many low-stake validators instead of acquiring a proportional share of stake.

This PR makes the threshold and vote counting stake-weighted:

  • Thresholdupdate_consensus_state now computes byzantine_threshold = floor(2 * total_stake / 3) + 1 (in stake units) instead of a count-based value.
  • Vote countingvote_on_proposal adds the voting validator's stake to vote_count on approval rather than a flat +1, so a proposal is approved only when the approving validators jointly control more than 2/3 of the total staked value.
  • vote_count, required_votes (BridgeProposal) and byzantine_threshold (ConsensusState), plus the matching ProposalCreatedEvent.required_votes / ProposalVotedEvent.vote_count event fields, are widened from u32 to i128 to hold stake magnitudes. EVENT_SCHEMA.md is updated to match.

This preserves the classic 2f+1-of-3f+1 safety margin, now expressed in stake terms.

Sybil resistance

Because the threshold scales with total_stake, registering additional low-stake validators raises the threshold proportionally, so validator count no longer buys an attacker any advantage — reaching quorum still requires a genuine 2/3 stake majority.

Tests

  • property_based_tests.rs: the BFT threshold property is rewritten for the stake-weighted formula (1 <= threshold <= total_stake), and a new property proves an adversary controlling ≤ 2/3 of the stake can never reach quorum regardless of how many Sybil validators it splits that stake across.
  • New unit test threshold_and_votes_are_stake_weighted_and_sybil_resistant registers one large-stake validator plus three minimum-stake validators and shows the three low-stake validators cannot reach the threshold, while the large-stake validator's vote pushes the approving stake over the line.

Acceptance criteria

  • Consensus threshold and vote counting are computed from validator stake, not validator count
  • Existing BFT property tests updated and still pass under the stake-weighted model
  • New test demonstrates Sybil resistance (many low-stake validators can't cheaply reach threshold)

…de#496)

update_consensus_state derived the Byzantine threshold from the active
validator *count* (floor(2n/3)+1), so an attacker could cheapen a Sybil
attack by registering many low-stake validators. Weight the threshold and
the vote tally by stake instead:

- byzantine_threshold = floor(2 * total_stake / 3) + 1 (stake units)
- vote_on_proposal adds the voting validator's stake to vote_count rather
  than a flat +1, so consensus requires approving validators to jointly
  control more than 2/3 of the total staked value
- vote_count / required_votes / byzantine_threshold widened to i128
- property tests updated to the stake-weighted formula and a new
  Sybil-resistance property; new unit test shows many low-stake validators
  cannot cheaply reach the threshold
The stake_threshold_resists_sybil_count property used prop_assume! to keep
the adversary's stake below 2/3 of the total, but that condition rejects the
large majority of generated inputs, so proptest aborted with too many
rejects. Replace the assumption with an equivalent tautology that holds for
every input: the adversary is either below the quorum threshold or genuinely
controls more than 2/3 of the total stake. No prop_assume, no rejections.
@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Thank you for contributing to the Project

@RUKAYAT-CODER
RUKAYAT-CODER merged commit a4bd8b1 into rinafcode:main Aug 17, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BFT consensus threshold is validator-count-weighted, not stake-weighted (Sybil risk)

2 participants