Skip to content

fix(consensus): equivocation detection + round-jump guard for order votes#727

Open
keanji-x wants to merge 1 commit into
mainfrom
kj/fix-41-43-order-vote
Open

fix(consensus): equivocation detection + round-jump guard for order votes#727
keanji-x wants to merge 1 commit into
mainfrom
kj/fix-41-43-order-vote

Conversation

@keanji-x
Copy link
Copy Markdown
Contributor

Summary

Minimal surgical fixes for two related gravity-audit findings in the order-vote path:

  • #41 (Critical)PendingOrderVotes now tracks author -> LedgerInfo digest and detects equivocation, mirroring PendingVotes::insert_vote. A Byzantine validator can no longer split its voting power across conflicting ordering candidates.
  • #43 (High)process_order_vote_msg now snapshots highest_ordered_round before the embedded QC is inserted and rejects votes whose round jumps more than MAX_ORDER_VOTE_ROUND_JUMP past that snapshot. Closes the fast-forward window where a single valid QC could be used to land an order vote for a round we never sequentially processed.

Why minimal (not a full backport of upstream #14637)

Upstream aptos-core PR #14637 covers both findings, but does so by reshaping li_digest_to_votes into a (QuorumCert, OrderVoteStatus) map and plumbing verified_quorum_cert: Option<QuorumCert> through insert_order_vote. That refactor conflicts on ~164 lines in our vendored consensus and changes call sites we'd then have to re-audit. This PR keeps the original types and only adds the security checks.

Test plan

  • cargo check -p aptos-consensus --tests passes.
  • Reviewer: confirm MAX_ORDER_VOTE_ROUND_JUMP value is safe vs. legitimate validator sync flows.
  • Reviewer: confirm equivocation log uses the same SecurityEvent::ConsensusEquivocatingVote channel as PendingVotes.

🤖 Generated with Claude Code

…ump guard in process_order_vote_msg

Closes #41 and #43 (gravity-audit).

#41: PendingOrderVotes had no per-author tracking, so a Byzantine
validator could submit order votes for multiple conflicting LedgerInfo
digests in the same round and have its voting power counted in each
bucket. Mirror the dedup pattern from PendingVotes:

- Add `author_to_li_digest: HashMap<Author, HashValue>` to PendingOrderVotes.
- In `insert_order_vote`, detect equivocation (same author, different digest)
  and return EquivocateVote with a SecurityEvent::ConsensusEquivocatingVote log.
- Garbage-collect author entries alongside their digest entries.

#43: process_order_vote_msg was the only message handler that didn't
call ensure_round_and_sync_up, so a Byzantine validator could ship an
OrderVoteMsg with a valid QC many rounds ahead of our local state,
fast-forward our round via the embedded QC's side-effect, then have the
attached order vote accepted for a round we never sequentially
processed. Since OrderVoteMsg carries no SyncInfo, the minimal
equivalent guard is to snapshot pre-state highest_ordered_round before
the QC insertion and reject the order vote if it would jump that
window by more than MAX_ORDER_VOTE_ROUND_JUMP.

This is a slice of upstream aptos-core PR #14637 ("Sync up QC in order
vote message"); we deliberately do NOT take the verified_quorum_cert
type refactor, which conflicts heavily with our tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant