feat: reject deprecated roles after Boole fork#794
feat: reject deprecated roles after Boole fork#794shane-moore wants to merge 4 commits intosigp:unstablefrom
Conversation
f892974 to
9a1ba52
Compare
|
@claude, feel free to take a look |
9a1ba52 to
c5201af
Compare
| // Reject deprecated roles after Boole fork | ||
| if matches!( | ||
| validation_context.role, | ||
| Role::Aggregator | Role::SyncCommittee | ||
| ) { | ||
| let epoch = messages.slot.epoch(validation_context.slots_per_epoch); | ||
| if validation_context.fork_schedule.active_fork(epoch) >= Fork::Boole { | ||
| return Err(ValidationFailure::RoleNotActiveAfterFork { | ||
| role: validation_context.role, | ||
| current_fork: validation_context.fork_schedule.active_fork(epoch), | ||
| deprecated_since_fork: Fork::Boole, | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Actually - can we deduplicate this by handling it somewhere else?
| let epoch = slot.epoch(validation_context.slots_per_epoch); | ||
| if validation_context.fork_schedule.active_fork(epoch) >= Fork::Boole { |
There was a problem hiding this comment.
I am not sure, but maybe we should use the fork indicated by the domain type instead?
There was a problem hiding this comment.
investigate a bit. seems we want to use the message's slot as the source of truth for determining which fork's validation rules apply
the DomainType we derive from a message id is actually something we validate against the expected fork derived from the slot in validate_topic_and_domain
anchor/anchor/message_validator/src/lib.rs
Lines 608 to 632 in c5201af
we basically want to validate that the DomainType from the message isn't from an incorrect topic. but use slot across the board to validate against
|
Claude finished @shane-moore's task in 3m 2s —— View job Code Review Complete ✅I've reviewed the PR that adds fork-aware validation to reject deprecated Critical: Code DuplicationThe fork validation logic is duplicated identically in two places:
This violates DRY principle and makes maintenance harder. I've added inline comments on both locations. The code also calls Recommendation: Extract into a helper function in fn validate_role_active_after_fork(
role: Role,
slot: Slot,
slots_per_epoch: u64,
fork_schedule: &ForkSchedule,
) -> Result<(), ValidationFailure> {
if matches!(role, Role::Aggregator | Role::SyncCommittee) {
let epoch = slot.epoch(slots_per_epoch);
let current_fork = fork_schedule.active_fork(epoch);
if current_fork >= Fork::Boole {
return Err(ValidationFailure::RoleNotActiveAfterFork {
role,
current_fork,
deprecated_since_fork: Fork::Boole,
});
}
}
Ok(())
}Then call it from both locations: validate_role_active_after_fork(
validation_context.role,
slot,
validation_context.slots_per_epoch,
&validation_context.fork_schedule,
)?;This addresses @dknopik's comment about deduplication. Review Comment: Domain Type vs Slot-Based ForkRegarding @dknopik's question about using the fork indicated by domain type: The current slot-based approach (slot → epoch → fork lookup) is correct and consistent with SIP-43 validation patterns used elsewhere in the codebase. The domain type in the message should already match the slot's fork (validated by Other FindingsPositive:
Suggestions:
SummaryThe code is functionally correct and follows Rust best practices. The primary issue is code duplication which should be addressed before merging. Once refactored, this is production-ready. Recommendation: Extract the duplicated validation logic into a helper function as shown above. |
| // Reject deprecated roles after Boole fork | ||
| if matches!( | ||
| validation_context.role, | ||
| Role::Aggregator | Role::SyncCommittee | ||
| ) { | ||
| let epoch = slot.epoch(validation_context.slots_per_epoch); | ||
| if validation_context.fork_schedule.active_fork(epoch) >= Fork::Boole { | ||
| return Err(ValidationFailure::RoleNotActiveAfterFork { | ||
| role: validation_context.role, | ||
| current_fork: validation_context.fork_schedule.active_fork(epoch), | ||
| deprecated_since_fork: Fork::Boole, | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Code Duplication: This validation logic is duplicated in partial_signature.rs:47-60. Consider extracting to a helper function validate_role_active_after_fork() in lib.rs to improve maintainability.
Also, cache the active_fork(epoch) result - it's currently called 3 times (lines 40, 54, 58) with the same arguments:
let epoch = slot.epoch(validation_context.slots_per_epoch);
let current_fork = validation_context.fork_schedule.active_fork(epoch);
if current_fork >= Fork::Boole {
return Err(ValidationFailure::RoleNotActiveAfterFork {
role: validation_context.role,
current_fork,
deprecated_since_fork: Fork::Boole,
});
}| // Reject deprecated roles after Boole fork | ||
| if matches!( | ||
| validation_context.role, | ||
| Role::Aggregator | Role::SyncCommittee | ||
| ) { | ||
| let epoch = messages.slot.epoch(validation_context.slots_per_epoch); | ||
| if validation_context.fork_schedule.active_fork(epoch) >= Fork::Boole { | ||
| return Err(ValidationFailure::RoleNotActiveAfterFork { | ||
| role: validation_context.role, | ||
| current_fork: validation_context.fork_schedule.active_fork(epoch), | ||
| deprecated_since_fork: Fork::Boole, | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Code Duplication: This validation logic is duplicated from consensus_message.rs:49-62. Extract to a shared helper function to avoid maintaining logic in two places.
Also cache active_fork(epoch) - it's called 3 times with same arguments (lines 38, 52, 56).
|
Claude finished @shane-moore's task in 1m 4s —— View job Code Review Complete ✅I've reviewed the PR after commit 7823568 and the code duplication issue has been successfully addressed! Excellent RefactoringThe duplicate fork validation logic has been extracted into a helper function
This is exactly what was recommended and eliminates the maintenance burden of duplicated logic. Review FindingsSafety & Best Practices:
Correctness:
Test Coverage:
Minor Suggestions
SummaryThis implementation is production-ready. The refactoring successfully addressed the code duplication concern while maintaining correctness and adding comprehensive test coverage. The slot-based validation approach is architecturally sound and aligns with SIP-43 validation patterns. Recommendation: ✅ Ready to merge (optional: add boundary test for extra confidence) |
|
@claude, i think we can do without hte boundary tests. there's no complex boundary behavior happening, i..e no intermediate states that could fail at transitions. and the |
|
Claude finished @shane-moore's task in 57s —— View job Agreed - Boundary Tests Not Required ✅You're absolutely right, Shane. After reviewing the code: 1. The validation logic is simple and deterministic:
2. ForkSchedule's unit tests already cover the boundary:
3. Your existing tests are sufficient:
Verdict: The separation of concerns is clean - Final Review Summary✅ Production Ready The refactoring successfully eliminated code duplication, and the implementation is architecturally sound. The slot-based validation approach aligns with SIP-43 patterns, and test coverage is comprehensive without being redundant. What was validated:
Recommendation: Ready to merge. |
Issue Addressed
Feature parity with ssv-go, catching up with their recent commits. This PR supports #770.
Proposed Changes
Reject deprecated
Role::AggregatorandRole::SyncCommitteemessages after the Boole fork activates. SSV-Go'svalidRoleAtSlot()rejects these roles post-Boole, so Anchor must do the same for P2P compatibility.RoleNotActiveAfterForkvalidation errorAdditional Info
N/A