@@ -936,4 +936,219 @@ mod tests {
936936 "TripleValidatorIndexInPartialSignatures" ,
937937 ) ;
938938 }
939+
940+ // TTL test constants
941+ const SLOTS_PER_EPOCH_TEST : u64 = 32 ;
942+ const LATE_SLOT_ALLOWANCE_TEST : u64 = 2 ;
943+ const TTL_SLOTS : u64 = SLOTS_PER_EPOCH_TEST + LATE_SLOT_ALLOWANCE_TEST ; // 34 slots
944+ const BEYOND_TTL_SLOTS : u64 = 40 ;
945+
946+ // Helper to create validation context for TTL tests
947+ fn create_ttl_validation_context < ' a > (
948+ signed_msg : & ' a SignedSSVMessage ,
949+ committee_info : & ' a crate :: CommitteeInfo ,
950+ role : Role ,
951+ operator_pub_keys : & ' a HashMap < OperatorId , Rsa < Public > > ,
952+ slots_late : u64 ,
953+ ) -> ValidationContext < ' a , ManualSlotClock > {
954+ let now = SystemTime :: now ( ) ;
955+ let slot_clock = ManualSlotClock :: new (
956+ Slot :: new ( 0 ) ,
957+ now. duration_since ( UNIX_EPOCH ) . unwrap ( ) ,
958+ Duration :: from_secs ( 12 ) ,
959+ ) ;
960+ slot_clock. advance_slot ( ) ;
961+
962+ ValidationContext {
963+ signed_ssv_message : signed_msg,
964+ committee_info,
965+ role,
966+ received_at : now + Duration :: from_secs ( 12 * slots_late) ,
967+ slots_per_epoch : SLOTS_PER_EPOCH_TEST ,
968+ epochs_per_sync_committee_period : 256 ,
969+ sync_committee_size : 512 ,
970+ slot_clock,
971+ operator_pub_keys,
972+ }
973+ }
974+
975+ // Helper to create a signed partial signature message for TTL tests
976+ fn create_signed_partial_sig_message (
977+ role : Role ,
978+ kind : PartialSignatureKind ,
979+ signer_id : OperatorId ,
980+ private_key : & Rsa < Private > ,
981+ ) -> SignedSSVMessage {
982+ let ( mut partial_sig_messages, _) = create_test_partial_signature (
983+ role,
984+ kind,
985+ signer_id,
986+ PartialSigTestOptions :: default ( ) ,
987+ Some ( private_key. clone ( ) ) ,
988+ ) ;
989+ partial_sig_messages. slot = Slot :: new ( 1 ) ;
990+
991+ let msg_id = create_message_id_for_test ( role) ;
992+ let ssv_msg = SSVMessage :: new (
993+ MsgType :: SSVPartialSignatureMsgType ,
994+ msg_id,
995+ partial_sig_messages. as_ssz_bytes ( ) ,
996+ )
997+ . unwrap ( ) ;
998+
999+ let p_key = PKey :: from_rsa ( private_key. clone ( ) ) . unwrap ( ) ;
1000+ let mut signer = Signer :: new ( MessageDigest :: sha256 ( ) , & p_key) . unwrap ( ) ;
1001+ signer. update ( & ssv_msg. as_ssz_bytes ( ) ) . unwrap ( ) ;
1002+ let signature = signer. sign_to_vec ( ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
1003+
1004+ SignedSSVMessage :: new ( vec ! [ signature] , vec ! [ signer_id] , ssv_msg, vec ! [ ] ) . unwrap ( )
1005+ }
1006+
1007+ #[ test]
1008+ fn test_validator_registration_within_ttl_accepted ( ) {
1009+ // Setup
1010+ let committee_info = create_committee_info ( FOUR_NODE_COMMITTEE ) ;
1011+ let ( private_key, public_key) = generate_test_key_pair ( ) ;
1012+ let map =
1013+ create_operator_pub_keys ( committee_info. committee_members . clone ( ) , vec ! [ public_key] ) ;
1014+ let signed_msg = create_signed_partial_sig_message (
1015+ Role :: ValidatorRegistration ,
1016+ PartialSignatureKind :: ValidatorRegistration ,
1017+ OperatorId ( 1 ) ,
1018+ & private_key,
1019+ ) ;
1020+
1021+ let validation_context = create_ttl_validation_context (
1022+ & signed_msg,
1023+ & committee_info,
1024+ Role :: ValidatorRegistration ,
1025+ & map,
1026+ TTL_SLOTS ,
1027+ ) ;
1028+
1029+ // Execute
1030+ let result = validate_partial_signature_message (
1031+ validation_context,
1032+ & mut DutyState :: new ( 64 ) ,
1033+ Arc :: new ( MockDutiesProvider {
1034+ voluntary_exit_duty_count : 0 ,
1035+ } ) ,
1036+ ) ;
1037+
1038+ // Assert
1039+ assert ! ( result. is_ok( ) , "Expected ok but got: {result:?}" ) ;
1040+ }
1041+
1042+ #[ test]
1043+ fn test_validator_registration_beyond_ttl_rejected ( ) {
1044+ // Setup
1045+ let committee_info = create_committee_info ( FOUR_NODE_COMMITTEE ) ;
1046+ let ( private_key, public_key) = generate_test_key_pair ( ) ;
1047+ let map =
1048+ create_operator_pub_keys ( committee_info. committee_members . clone ( ) , vec ! [ public_key] ) ;
1049+ let signed_msg = create_signed_partial_sig_message (
1050+ Role :: ValidatorRegistration ,
1051+ PartialSignatureKind :: ValidatorRegistration ,
1052+ OperatorId ( 1 ) ,
1053+ & private_key,
1054+ ) ;
1055+
1056+ let validation_context = create_ttl_validation_context (
1057+ & signed_msg,
1058+ & committee_info,
1059+ Role :: ValidatorRegistration ,
1060+ & map,
1061+ BEYOND_TTL_SLOTS ,
1062+ ) ;
1063+
1064+ // Execute
1065+ let result = validate_partial_signature_message (
1066+ validation_context,
1067+ & mut DutyState :: new ( 64 ) ,
1068+ Arc :: new ( MockDutiesProvider {
1069+ voluntary_exit_duty_count : 0 ,
1070+ } ) ,
1071+ ) ;
1072+
1073+ // Assert
1074+ assert_validation_error (
1075+ result,
1076+ |failure| matches ! ( failure, ValidationFailure :: LateSlotMessage { .. } ) ,
1077+ "LateSlotMessage" ,
1078+ ) ;
1079+ }
1080+
1081+ #[ test]
1082+ fn test_voluntary_exit_within_ttl_accepted ( ) {
1083+ // Setup
1084+ let committee_info = create_committee_info ( FOUR_NODE_COMMITTEE ) ;
1085+ let ( private_key, public_key) = generate_test_key_pair ( ) ;
1086+ let map =
1087+ create_operator_pub_keys ( committee_info. committee_members . clone ( ) , vec ! [ public_key] ) ;
1088+ let signed_msg = create_signed_partial_sig_message (
1089+ Role :: VoluntaryExit ,
1090+ PartialSignatureKind :: VoluntaryExit ,
1091+ OperatorId ( 1 ) ,
1092+ & private_key,
1093+ ) ;
1094+
1095+ let validation_context = create_ttl_validation_context (
1096+ & signed_msg,
1097+ & committee_info,
1098+ Role :: VoluntaryExit ,
1099+ & map,
1100+ TTL_SLOTS ,
1101+ ) ;
1102+
1103+ // Execute
1104+ let result = validate_partial_signature_message (
1105+ validation_context,
1106+ & mut DutyState :: new ( 64 ) ,
1107+ Arc :: new ( MockDutiesProvider {
1108+ voluntary_exit_duty_count : 1 ,
1109+ } ) ,
1110+ ) ;
1111+
1112+ // Assert
1113+ assert ! ( result. is_ok( ) , "Expected ok but got: {result:?}" ) ;
1114+ }
1115+
1116+ #[ test]
1117+ fn test_voluntary_exit_beyond_ttl_rejected ( ) {
1118+ // Setup
1119+ let committee_info = create_committee_info ( FOUR_NODE_COMMITTEE ) ;
1120+ let ( private_key, public_key) = generate_test_key_pair ( ) ;
1121+ let map =
1122+ create_operator_pub_keys ( committee_info. committee_members . clone ( ) , vec ! [ public_key] ) ;
1123+ let signed_msg = create_signed_partial_sig_message (
1124+ Role :: VoluntaryExit ,
1125+ PartialSignatureKind :: VoluntaryExit ,
1126+ OperatorId ( 1 ) ,
1127+ & private_key,
1128+ ) ;
1129+
1130+ let validation_context = create_ttl_validation_context (
1131+ & signed_msg,
1132+ & committee_info,
1133+ Role :: VoluntaryExit ,
1134+ & map,
1135+ BEYOND_TTL_SLOTS ,
1136+ ) ;
1137+
1138+ // Execute
1139+ let result = validate_partial_signature_message (
1140+ validation_context,
1141+ & mut DutyState :: new ( 64 ) ,
1142+ Arc :: new ( MockDutiesProvider {
1143+ voluntary_exit_duty_count : 1 ,
1144+ } ) ,
1145+ ) ;
1146+
1147+ // Assert
1148+ assert_validation_error (
1149+ result,
1150+ |failure| matches ! ( failure, ValidationFailure :: LateSlotMessage { .. } ) ,
1151+ "LateSlotMessage" ,
1152+ ) ;
1153+ }
9391154}
0 commit comments