-
Notifications
You must be signed in to change notification settings - Fork 27
fix(qbft): validate round change justification consistency with data_round #567
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
fix(qbft): validate round change justification consistency with data_round #567
Conversation
…round Fix critical consensus vulnerability where malicious nodes could include unvalidated prepare messages in round changes claiming no preparation (data_round=0). Changes: - Add validation in validate_proposal_justifications() to reject round changes with data_round=0 but non-empty prepare justifications - Add validation in received_round_change() for the same vulnerability - Add comprehensive test verifying both malicious and valid cases According to EEA QBFT v1 specification: - If data_round == 0: No prepare justifications should be present - If data_round > 0: Prepare justifications MUST be validated This prevents consensus safety violations where unvalidated prepare messages could bypass validation and corrupt justification logic.
9e40627 to
adcd9dd
Compare
- Clarify that prepare justifications are used to justify the highest prepared value from round changes - Fix incomplete comment that was cut off mid-sentence
…s://github.com/diegomrsantos/anchor into fix/qbft-round-change-justification-validation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a critical QBFT consensus vulnerability by implementing validation for round change message consistency with the EEA QBFT v1 specification. The fix ensures that nodes claiming no preparation (data_round == 0) cannot include prepare message justifications, preventing malicious nodes from bypassing consensus safety checks.
- Adds validation in
validate_proposal_justifications()to reject round change messages with inconsistentdata_roundand justification fields - Adds validation in
received_round_change()with the same consistency checks - Includes comprehensive test coverage verifying the vulnerability fix and valid message acceptance
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| anchor/common/qbft/src/lib.rs | Implements security validation in two functions to ensure round change message consistency between data_round and justifications |
| anchor/common/qbft/src/tests.rs | Adds comprehensive test verifying the vulnerability fix by testing both malicious and valid round change scenarios |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
anchor/common/qbft/src/tests.rs
Outdated
| round_change_justification: vec![signed_malicious_prepare], // BUT includes justifications! | ||
| prepare_justification: vec![], |
Copilot
AI
Sep 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is using round_change_justification field to include prepare messages, but based on the validation logic in lib.rs, this should be checking if prepare justifications are included in a round change message. The field name suggests this should contain round change messages, not prepare messages.
| round_change_justification: vec![signed_malicious_prepare], // BUT includes justifications! | |
| prepare_justification: vec![], | |
| round_change_justification: vec![], // Should only include round change messages | |
| prepare_justification: vec![signed_malicious_prepare], // Maliciously includes prepare justification! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dknopik how about this?
Zacholme7
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense
|
my bad, didnt see this was draft |
I changed to draft right now, I wanna give another review on this tomorrow with a fresh mind. But thanks anyway ;) |
|
before merging this, let me test this against some fuzzer inputs I saved 🙏 |
- Move doc comments above #[test] attributes for proper documentation - Convert // comments to /// doc comments for test functions - Ensure consistent documentation style across all tests
- Extract helper functions to reduce test duplication: - create_signed_ssv_message(): Creates SSVMessage boilerplate - create_wrapped_round_change(): Creates round change messages - test_round_change_acceptance(): Tests acceptance/rejection logic - Reduce test from ~200 lines to ~90 lines - Improve maintainability and readability - Same test coverage with cleaner code
- Added DRY principle guidelines to Code Style section - Added detailed Testing Guidelines with code duplication prevention - Emphasized immediate refactoring when duplication is detected - Added proper test documentation placement requirements - Included helper function extraction patterns and examples Prevents future code duplication issues by establishing clear guidelines for when and how to extract helper functions.
- Added guidance to replace/consolidate rather than always add new content - Emphasized concise, essential-only updates - Added periodic review process for file maintenance - Prioritized quality over quantity in guidelines Prevents instruction files from becoming unwieldy over time.
…erence - Added mandatory format & lint step before all commits - Removed 'commit without permission' to respect user preferences - Ensures code quality standards are maintained in learning updates
…ication - Streamline CLAUDE.md testing section to eliminate duplication with tester-subagent - Add comprehensive proactive duplication prevention methodology to tester-subagent - Enforce design-first approach: plan helper functions before writing tests - Add enforcement rule: extract helpers immediately at 3+ line duplication - Replace reactive cleanup approach with mandatory upfront pattern identification
|
Related ssv spec issue: ssvlabs/ssv-spec#582 It sounds like this is to be added as message validation rule (i.e. in |
Isn't it different? |
|
My initial idea was to implement this kind of validation in the QBFT crate, but I don't recall why I changed my mind. The advantage of having it in the validator is that we reject them faster and penalize the node, but not having this in the QBFT feels wrong. |
Oh, right, it is kinda different. I guess either all three or none of those should be true: a) has full data so it is kinda related? |
Maybe. How does |
|
Some required checks have failed. Could you please take a look @diegomrsantos? 🙏 |
8292880 to
038f9db
Compare
I believe to |
|
Hi @diegomrsantos, this pull request has been closed automatically due to 30 days of inactivity. If you’d like to continue working on it, feel free to reopen at any time. |
Issue Addressed
This PR improves QBFT round change message handling by implementing defensive validation that aligns with the EEA QBFT v1 specification's construction patterns.
Proposed Changes
validate_proposal_justifications()to ensure round change messages follow expected construction patternsreceived_round_change()for consistencyvalidate_proposal_justificationsfunctionImplementation Improvement:
This validation implements defense-in-depth by ensuring:
data_round == 0) have empty justifications (following spec construction intent)Specification Alignment
EEA QBFT v1 Construction Patterns
While the
validRoundChangepredicate only constrains payload fields (preparedRound/preparedValue), the specification's construction functions show the intended message shape:createRoundChangesetsroundChangeJustificationto{}when there's no prepared blockgetRoundChangeJustificationreturns{}for nodes with no preparationProposal Validation Logic
The
isProposalJustificationpredicate handles gating based on round change payload claims:preparedRound/preparedValue, enters unprepared branch and ignores preparespreparedRound=Noneare simply ignored during validationImplementation Rationale
This validation provides:
Additional Info
This change represents a defensive implementation improvement that:
validRoundChangepredicate requirementsThe validation mirrors the specification's construction patterns while providing additional implementation robustness.