Skip to content

Commit 2ff8b27

Browse files
apollo_consensus: SM tracks last self votes not the SHC
1 parent eba6d0f commit 2ff8b27

File tree

3 files changed

+48
-42
lines changed

3 files changed

+48
-42
lines changed

crates/apollo_consensus/src/single_height_consensus.rs

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,6 @@ pub(crate) struct SingleHeightConsensus {
139139
state_machine: StateMachine,
140140
// Tracks rounds for which we started validating a proposal to avoid duplicate validations.
141141
pending_validation_rounds: HashSet<Round>,
142-
last_prevote: Option<Vote>,
143-
last_precommit: Option<Vote>,
144142
height_voted_storage: Arc<Mutex<dyn HeightVotedStorageTrait>>,
145143
}
146144

@@ -163,8 +161,6 @@ impl SingleHeightConsensus {
163161
timeouts,
164162
state_machine,
165163
pending_validation_rounds: HashSet::new(),
166-
last_prevote: None,
167-
last_precommit: None,
168164
height_voted_storage,
169165
}
170166
}
@@ -247,7 +243,7 @@ impl SingleHeightConsensus {
247243
self.handle_timeout(context, event).await
248244
}
249245
StateMachineEvent::Prevote(vote) => {
250-
let Some(last_vote) = &self.last_prevote else {
246+
let Some(last_vote) = self.state_machine.last_self_prevote() else {
251247
return Err(ConsensusError::InternalInconsistency(
252248
"No prevote to send".to_string(),
253249
));
@@ -264,7 +260,7 @@ impl SingleHeightConsensus {
264260
)]))
265261
}
266262
StateMachineEvent::Precommit(vote) => {
267-
let Some(last_vote) = &self.last_precommit else {
263+
let Some(last_vote) = self.state_machine.last_self_precommit() else {
268264
return Err(ConsensusError::InternalInconsistency(
269265
"No precommit to send".to_string(),
270266
));
@@ -503,37 +499,18 @@ impl SingleHeightConsensus {
503499
context: &mut ContextT,
504500
vote: Vote,
505501
) -> Result<Vec<ShcTask>, ConsensusError> {
506-
let (last_vote, task) = match vote.vote_type {
507-
VoteType::Prevote => (
508-
&mut self.last_prevote,
509-
ShcTask::Prevote(
510-
self.timeouts.get_prevote_timeout(0),
511-
StateMachineEvent::Prevote(vote.clone()),
512-
),
502+
let task = match vote.vote_type {
503+
VoteType::Prevote => ShcTask::Prevote(
504+
self.timeouts.get_prevote_timeout(0),
505+
StateMachineEvent::Prevote(vote.clone()),
513506
),
514-
VoteType::Precommit => (
515-
&mut self.last_precommit,
516-
ShcTask::Precommit(
517-
self.timeouts.get_precommit_timeout(0),
518-
StateMachineEvent::Precommit(vote.clone()),
519-
),
507+
VoteType::Precommit => ShcTask::Precommit(
508+
self.timeouts.get_precommit_timeout(0),
509+
StateMachineEvent::Precommit(vote.clone()),
520510
),
521511
};
522512
// Ensure the voter matches this node.
523513
assert_eq!(vote.voter, self.state_machine.validator_id());
524-
*last_vote = match last_vote {
525-
None => Some(vote.clone()),
526-
Some(last_vote) if vote.round > last_vote.round => Some(vote.clone()),
527-
Some(_) => {
528-
// According to the Tendermint paper, the state machine should only vote for its
529-
// current round. It should monotonically increase its round. It should only vote
530-
// once per step.
531-
return Err(ConsensusError::InternalInconsistency(format!(
532-
"State machine must progress in time: last_vote: {last_vote:?} new_vote: \
533-
{vote:?}",
534-
)));
535-
}
536-
};
537514

538515
trace!("Writing voted height {} to storage", self.state_machine.height());
539516
self.height_voted_storage

crates/apollo_consensus/src/state_machine.rs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ pub(crate) struct StateMachine {
9999
prevote_quorum: HashSet<Round>,
100100
mixed_prevote_quorum: HashSet<Round>,
101101
mixed_precommit_quorum: HashSet<Round>,
102+
// Tracks the latest self votes for efficient rebroadcasts.
103+
last_self_prevote: Option<Vote>,
104+
last_self_precommit: Option<Vote>,
102105
}
103106

104107
impl StateMachine {
@@ -131,6 +134,8 @@ impl StateMachine {
131134
prevote_quorum: HashSet::new(),
132135
mixed_prevote_quorum: HashSet::new(),
133136
mixed_precommit_quorum: HashSet::new(),
137+
last_self_prevote: None,
138+
last_self_precommit: None,
134139
}
135140
}
136141

@@ -170,13 +175,37 @@ impl StateMachine {
170175
self.proposals.get(&round).and_then(|(id, _)| *id)
171176
}
172177

178+
pub(crate) fn last_self_prevote(&self) -> Option<Vote> {
179+
self.last_self_prevote.clone()
180+
}
181+
182+
pub(crate) fn last_self_precommit(&self) -> Option<Vote> {
183+
self.last_self_precommit.clone()
184+
}
185+
173186
fn make_self_vote(
174-
&self,
187+
&mut self,
175188
vote_type: VoteType,
176-
round: Round,
177189
proposal_commitment: Option<ProposalCommitment>,
178190
) -> Vote {
179-
Vote { vote_type, height: self.height.0, round, proposal_commitment, voter: self.id }
191+
let vote = Vote {
192+
vote_type,
193+
height: self.height.0,
194+
round: self.round,
195+
proposal_commitment,
196+
voter: self.id,
197+
};
198+
// update the latest self vote.
199+
let last_self_vote = match vote_type {
200+
VoteType::Prevote => &mut self.last_self_prevote,
201+
VoteType::Precommit => &mut self.last_self_precommit,
202+
};
203+
assert!(
204+
last_self_vote.as_ref().is_none_or(|last| self.round > last.round),
205+
"State machine must progress in time: last_vote: {last_self_vote:?} new_vote: {vote:?}"
206+
);
207+
*last_self_vote = Some(vote.clone());
208+
vote
180209
}
181210

182211
/// Starts the state machine, effectively calling `StartRound(0)` from the paper. This is
@@ -338,7 +367,7 @@ impl StateMachine {
338367
round={round}."
339368
);
340369
CONSENSUS_TIMEOUTS.increment(1, &[(LABEL_NAME_TIMEOUT_TYPE, TimeoutType::Propose.into())]);
341-
let vote = self.make_self_vote(VoteType::Prevote, round, None);
370+
let vote = self.make_self_vote(VoteType::Prevote, None);
342371
let mut output = VecDeque::from([StateMachineEvent::Prevote(vote)]);
343372
output.append(&mut self.advance_to_step(Step::Prevote));
344373
output
@@ -370,7 +399,7 @@ impl StateMachine {
370399
};
371400
debug!("Applying TimeoutPrevote for round={round}.");
372401
CONSENSUS_TIMEOUTS.increment(1, &[(LABEL_NAME_TIMEOUT_TYPE, TimeoutType::Prevote.into())]);
373-
let vote = self.make_self_vote(VoteType::Precommit, round, None);
402+
let vote = self.make_self_vote(VoteType::Precommit, None);
374403
let mut output = VecDeque::from([StateMachineEvent::Precommit(vote)]);
375404
output.append(&mut self.advance_to_step(Step::Precommit));
376405
output
@@ -518,7 +547,7 @@ impl StateMachine {
518547
} else {
519548
None
520549
};
521-
let vote = self.make_self_vote(VoteType::Prevote, self.round, vote_commitment);
550+
let vote = self.make_self_vote(VoteType::Prevote, vote_commitment);
522551
let mut output = VecDeque::from([StateMachineEvent::Prevote(vote)]);
523552
output.append(&mut self.advance_to_step(Step::Prevote));
524553
output
@@ -550,7 +579,7 @@ impl StateMachine {
550579
} else {
551580
None
552581
};
553-
let vote = self.make_self_vote(VoteType::Prevote, self.round, vote_commitment);
582+
let vote = self.make_self_vote(VoteType::Prevote, vote_commitment);
554583
let mut output = VecDeque::from([StateMachineEvent::Prevote(vote)]);
555584
output.append(&mut self.advance_to_step(Step::Prevote));
556585
output
@@ -600,7 +629,7 @@ impl StateMachine {
600629
CONSENSUS_NEW_VALUE_LOCKS.increment(1);
601630
}
602631
self.locked_value_round = new_value;
603-
let vote = self.make_self_vote(VoteType::Precommit, self.round, Some(*proposal_id));
632+
let vote = self.make_self_vote(VoteType::Precommit, Some(*proposal_id));
604633
let mut output = VecDeque::from([StateMachineEvent::Precommit(vote)]);
605634
output.append(&mut self.advance_to_step(Step::Precommit));
606635
output
@@ -614,7 +643,7 @@ impl StateMachine {
614643
if !self.value_has_enough_votes(&self.prevotes, self.round, &None, &self.quorum) {
615644
return VecDeque::new();
616645
}
617-
let vote = self.make_self_vote(VoteType::Precommit, self.round, None);
646+
let vote = self.make_self_vote(VoteType::Precommit, None);
618647
let mut output = VecDeque::from([StateMachineEvent::Precommit(vote)]);
619648
output.append(&mut self.advance_to_step(Step::Precommit));
620649
output

crates/apollo_protobuf/src/consensus.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl<T> IntoFromProto for T where
3737
{
3838
}
3939

40-
#[derive(Debug, Default, Hash, Clone, Eq, PartialEq, Serialize, Deserialize)]
40+
#[derive(Debug, Default, Hash, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
4141
pub enum VoteType {
4242
Prevote,
4343
#[default]

0 commit comments

Comments
 (0)