Skip to content

Commit c56dd46

Browse files
Merge branch 'unstable' into feat/operator-doppelganger-protection
2 parents cd826ae + 51402a0 commit c56dd46

File tree

1 file changed

+115
-10
lines changed

1 file changed

+115
-10
lines changed

anchor/message_validator/src/partial_signature.rs

Lines changed: 115 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,14 @@ fn validate_partial_sig_messages_by_duty_logic(
248248
}
249249
}
250250
}
251-
Role::SyncCommittee if message_count > MAX_SIGNATURES_IN_SYNC_COMMITTEE => {
252-
// Rule: Number of signatures must be <= MAX_SIGNATURES_IN_SYNC_COMMITTEE
253-
return Err(ValidationFailure::TooManyPartialSignatureMessages {
254-
got: message_count,
255-
limit: MAX_SIGNATURES_IN_SYNC_COMMITTEE,
256-
});
251+
Role::SyncCommittee => {
252+
if message_count > MAX_SIGNATURES_IN_SYNC_COMMITTEE {
253+
// Rule: Number of signatures must be <= MAX_SIGNATURES_IN_SYNC_COMMITTEE
254+
return Err(ValidationFailure::TooManyPartialSignatureMessages {
255+
got: message_count,
256+
limit: MAX_SIGNATURES_IN_SYNC_COMMITTEE,
257+
});
258+
}
257259
}
258260
_ if message_count > 1 => {
259261
// Rule: For other duties, only one signature is allowed
@@ -715,9 +717,9 @@ mod tests {
715717
);
716718
}
717719

718-
fn create_partial_signature_messages() -> Vec<PartialSignatureMessage> {
720+
fn create_partial_signature_messages_with_count(count: usize) -> Vec<PartialSignatureMessage> {
719721
let mut messages = vec![];
720-
for _ in 0..3 {
722+
for _ in 0..count {
721723
messages.push(PartialSignatureMessage {
722724
partial_signature: Signature::empty(),
723725
signing_root: Hash256::from([0u8; 32]),
@@ -728,12 +730,62 @@ mod tests {
728730
messages
729731
}
730732

733+
/// Helper function to validate sync committee partial signatures with a given message count.
734+
/// Returns the validation result for assertion in individual tests.
735+
fn validate_sync_committee_signature_count(
736+
message_count: usize,
737+
) -> Result<ValidatedSSVMessage, ValidationFailure> {
738+
let committee_info = create_committee_info(FOUR_NODE_COMMITTEE);
739+
740+
let messages = create_partial_signature_messages_with_count(message_count);
741+
742+
let partial_sig_messages = PartialSignatureMessages {
743+
kind: PartialSignatureKind::PostConsensus,
744+
slot: Slot::new(0),
745+
messages: messages.into(),
746+
};
747+
748+
let msg_id = create_message_id_for_test(Role::SyncCommittee);
749+
let ssv_msg_data = partial_sig_messages.as_ssz_bytes();
750+
let ssv_msg = SSVMessage::new(MsgType::SSVPartialSignatureMsgType, msg_id, ssv_msg_data)
751+
.expect("SSVMessage should be created");
752+
753+
let (private_key, public_key) = generate_test_key_pair();
754+
let p_key = PKey::from_rsa(private_key).unwrap();
755+
let mut signer = Signer::new(MessageDigest::sha256(), &p_key).unwrap();
756+
signer.update(&ssv_msg.as_ssz_bytes()).unwrap();
757+
let signature = vec![
758+
signer
759+
.sign_to_vec()
760+
.expect("Failed to sign message")
761+
.try_into()
762+
.expect("Signature should be 256 bytes"),
763+
];
764+
765+
let signed_msg = SignedSSVMessage::new(signature, vec![OperatorId(1)], ssv_msg, vec![])
766+
.expect("SignedSSVMessage should be created");
767+
768+
let map =
769+
create_operator_pub_keys(committee_info.committee_members.clone(), vec![public_key]);
770+
771+
let validation_context =
772+
create_test_validation_context(&signed_msg, &committee_info, Role::SyncCommittee, &map);
773+
774+
validate_partial_signature_message(
775+
validation_context,
776+
&mut DutyState::new(2),
777+
Arc::new(MockDutiesProvider {
778+
voluntary_exit_duty_count: 0,
779+
}),
780+
)
781+
}
782+
731783
#[test]
732784
fn test_too_many_partial_signature_messages() {
733785
let committee_info = create_committee_info(FOUR_NODE_COMMITTEE);
734786

735787
// Create messages with more than allowed count
736-
let messages = create_partial_signature_messages();
788+
let messages = create_partial_signature_messages_with_count(3);
737789

738790
let partial_sig_messages = PartialSignatureMessages {
739791
kind: PartialSignatureKind::PostConsensus,
@@ -780,12 +832,65 @@ mod tests {
780832
);
781833
}
782834

835+
#[test]
836+
fn test_sync_committee_accepts_multiple_signatures_within_limit() {
837+
// Test 3 signatures (well within the limit)
838+
let result = validate_sync_committee_signature_count(3);
839+
840+
assert!(
841+
result.is_ok(),
842+
"{}",
843+
format!("Expected successful validation but got: {result:?}")
844+
);
845+
}
846+
847+
#[test]
848+
fn test_sync_committee_accepts_max_signatures() {
849+
// Test exactly 13 signatures (at the limit)
850+
let result = validate_sync_committee_signature_count(13);
851+
852+
assert!(
853+
result.is_ok(),
854+
"{}",
855+
format!("Expected successful validation for 13 signatures but got: {result:?}")
856+
);
857+
}
858+
859+
#[test]
860+
fn test_sync_committee_rejects_too_many_signatures() {
861+
// Test 14 signatures (one over the limit)
862+
let result = validate_sync_committee_signature_count(14);
863+
864+
assert_validation_error(
865+
result,
866+
|failure| {
867+
matches!(
868+
failure,
869+
ValidationFailure::TooManyPartialSignatureMessages { got: 14, limit: 13 }
870+
)
871+
},
872+
"TooManyPartialSignatureMessages with got=14, limit=13",
873+
);
874+
}
875+
876+
#[test]
877+
fn test_sync_committee_accepts_single_signature() {
878+
// Test 1 signature (minimal case)
879+
let result = validate_sync_committee_signature_count(1);
880+
881+
assert!(
882+
result.is_ok(),
883+
"{}",
884+
format!("Expected successful validation for single signature but got: {result:?}")
885+
);
886+
}
887+
783888
#[test]
784889
fn test_triple_validator_index_fails() {
785890
let committee_info = create_committee_info(FOUR_NODE_COMMITTEE);
786891

787892
// Create messages with a validator index that appears 3 times
788-
let messages = create_partial_signature_messages();
893+
let messages = create_partial_signature_messages_with_count(3);
789894

790895
let partial_sig_messages = PartialSignatureMessages {
791896
kind: PartialSignatureKind::PostConsensus,

0 commit comments

Comments
 (0)