Goal
Pin the behavioral half of the per-peer duplicate fix from #1131 / PR #1203: NetworkMessageReceiver::receive must pass Some(propagation_source) (not None) into Validator::validate (message_receiver/src/manager.rs:83). If that regresses to None, received_from.is_some() is false for every inbound message, so every role-8 duplicate classifies as the Ignore-class relay and the whole existing suite still passes.
Why it isn't covered today
The per-peer matrix tests in #1203 call validate_partial_signature_message directly with their own PeerId, so they never exercise the receive seam. A receive-level guard needs a real role-8 SignedSSVMessage that survives full validation — including RSA verification against the operator pubkey seeded into NetworkState. That requires a matched pair: the message signed by the same key whose pubkey is in the DB. The fixtures that build this (create_signed_proposer_preferences_message, create_operator_pub_keys, MockDutiesProvider, create_committee_info) are pub(crate) inside message_validator's #[cfg(test)] module and are unreachable from message_receiver; message_validator has no test-utils feature to expose them.
Suggested approach
- Add a
test-utils feature to message_validator that exposes the role-8 message / committee / duties fixtures, mirroring database's existing test-utils feature.
- Add a
message_receiver test (dev-dep on message_validator with test-utils and database test-utils): construct a NetworkMessageReceiver, spawn a processor, is_synced = true, seed a committee/share into NetworkState, then call receive twice from the same peer with the same message and assert the second Outcome.action is Reject.
Acceptance criteria
- The new test fails if
manager.rs:83 is changed to None.
- No production logic change beyond gating existing test helpers behind the
test-utils feature.
Notes
Goal
Pin the behavioral half of the per-peer duplicate fix from #1131 / PR #1203:
NetworkMessageReceiver::receivemust passSome(propagation_source)(notNone) intoValidator::validate(message_receiver/src/manager.rs:83). If that regresses toNone,received_from.is_some()is false for every inbound message, so every role-8 duplicate classifies as the Ignore-class relay and the whole existing suite still passes.Why it isn't covered today
The per-peer matrix tests in #1203 call
validate_partial_signature_messagedirectly with their ownPeerId, so they never exercise thereceiveseam. Areceive-level guard needs a real role-8SignedSSVMessagethat survives full validation — including RSA verification against the operator pubkey seeded intoNetworkState. That requires a matched pair: the message signed by the same key whose pubkey is in the DB. The fixtures that build this (create_signed_proposer_preferences_message,create_operator_pub_keys,MockDutiesProvider,create_committee_info) arepub(crate)insidemessage_validator's#[cfg(test)]module and are unreachable frommessage_receiver;message_validatorhas notest-utilsfeature to expose them.Suggested approach
test-utilsfeature tomessage_validatorthat exposes the role-8 message / committee / duties fixtures, mirroringdatabase's existingtest-utilsfeature.message_receivertest (dev-dep onmessage_validatorwithtest-utilsanddatabasetest-utils): construct aNetworkMessageReceiver, spawn a processor,is_synced = true, seed a committee/share intoNetworkState, then callreceivetwice from the same peer with the same message and assert the secondOutcome.actionisReject.Acceptance criteria
manager.rs:83is changed toNone.test-utilsfeature.Notes