Skip to content

Commit 2b5ed03

Browse files
apollo_consensus: add observer_does_not_record_self_votes test (#10340)
1 parent db31478 commit 2b5ed03

File tree

2 files changed

+37
-3
lines changed

2 files changed

+37
-3
lines changed

crates/apollo_consensus/src/state_machine.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ pub(crate) enum StateMachineEvent {
4848
Prevote(Vote),
4949
/// Precommit message, sent from the SHC to the state machine.
5050
Precommit(Vote),
51-
/// TimeoutPropose event, sent from the state machine to the SHC.
51+
/// TimeoutPropose event, sent from the SHC to the state machine.
5252
TimeoutPropose(Round),
53-
/// TimeoutPrevote event, sent from the state machine to the SHC.
53+
/// TimeoutPrevote event, sent from the SHC to the state machine.
5454
TimeoutPrevote(Round),
55-
/// TimeoutPrecommit event, sent from the state machine to the SHC.
55+
/// TimeoutPrecommit event, sent from the SHC to the state machine.
5656
TimeoutPrecommit(Round),
5757
/// Used by the SHC to rebroadcast a self-vote.
5858
RebroadcastVote(Vote),

crates/apollo_consensus/src/state_machine_test.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,3 +610,37 @@ fn number_of_required_votes(quorum_type: QuorumType) {
610610
);
611611
assert!(wrapper.next_request().is_none());
612612
}
613+
614+
#[test]
615+
fn observer_does_not_record_self_votes() {
616+
// Set up as an observer.
617+
let id = *VALIDATOR_ID;
618+
let mut wrapper = TestWrapper::new(id, 4, |_: Round| *PROPOSER_ID, true, QuorumType::Byzantine);
619+
620+
// Start and receive proposal validation completion.
621+
wrapper.start();
622+
assert_eq!(wrapper.next_request().unwrap(), SMRequest::ScheduleTimeoutPropose(ROUND));
623+
assert!(wrapper.next_request().is_none());
624+
wrapper.send_finished_validation(PROPOSAL_ID, ROUND);
625+
626+
// Reach mixed prevote quorum with peer votes only (self not counted).
627+
wrapper.send_prevote(PROPOSAL_ID, ROUND);
628+
wrapper.send_prevote(PROPOSAL_ID, ROUND);
629+
// No quorum yet, we didn't vote.
630+
assert!(wrapper.next_request().is_none());
631+
wrapper.send_prevote(PROPOSAL_ID, ROUND);
632+
assert_eq!(wrapper.next_request().unwrap(), SMRequest::ScheduleTimeoutPrevote(ROUND));
633+
634+
// Timeout prevote triggers self precommit(nil) path, which observers must not record/broadcast.
635+
wrapper.send_timeout_prevote(ROUND);
636+
assert!(wrapper.next_request().is_none());
637+
assert_eq!(wrapper.state_machine.last_self_precommit(), None);
638+
639+
// Reach mixed precommit quorum with peer votes only and ensure timeout is scheduled.
640+
wrapper.send_precommit(PROPOSAL_ID, ROUND);
641+
wrapper.send_precommit(PROPOSAL_ID, ROUND);
642+
// No quorum yet, we didn't vote.
643+
assert!(wrapper.next_request().is_none());
644+
wrapper.send_precommit(PROPOSAL_ID, ROUND);
645+
assert_eq!(wrapper.next_request().unwrap(), SMRequest::ScheduleTimeoutPrecommit(ROUND));
646+
}

0 commit comments

Comments
 (0)