Skip to content

Commit bc2d9be

Browse files
apollo_consensus: SM tracks last self votes not the SHC
1 parent 31bd3d1 commit bc2d9be

File tree

2 files changed

+43
-34
lines changed

2 files changed

+43
-34
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
));
@@ -502,37 +498,18 @@ impl SingleHeightConsensus {
502498
context: &mut ContextT,
503499
vote: Vote,
504500
) -> Result<Vec<ShcTask>, ConsensusError> {
505-
let (last_vote, task) = match vote.vote_type {
506-
VoteType::Prevote => (
507-
&mut self.last_prevote,
508-
ShcTask::Prevote(
509-
self.timeouts.get_prevote_timeout(0),
510-
StateMachineEvent::Prevote(vote.clone()),
511-
),
501+
let task = match vote.vote_type {
502+
VoteType::Prevote => ShcTask::Prevote(
503+
self.timeouts.get_prevote_timeout(0),
504+
StateMachineEvent::Prevote(vote.clone()),
512505
),
513-
VoteType::Precommit => (
514-
&mut self.last_precommit,
515-
ShcTask::Precommit(
516-
self.timeouts.get_precommit_timeout(0),
517-
StateMachineEvent::Precommit(vote.clone()),
518-
),
506+
VoteType::Precommit => ShcTask::Precommit(
507+
self.timeouts.get_precommit_timeout(0),
508+
StateMachineEvent::Precommit(vote.clone()),
519509
),
520510
};
521511
// Ensure the voter matches this node.
522512
assert_eq!(vote.voter, self.state_machine.validator_id());
523-
*last_vote = match last_vote {
524-
None => Some(vote.clone()),
525-
Some(last_vote) if vote.round > last_vote.round => Some(vote.clone()),
526-
Some(_) => {
527-
// According to the Tendermint paper, the state machine should only vote for its
528-
// current round. It should monotonically increase its round. It should only vote
529-
// once per step.
530-
return Err(ConsensusError::InternalInconsistency(format!(
531-
"State machine must progress in time: last_vote: {last_vote:?} new_vote: \
532-
{vote:?}",
533-
)));
534-
}
535-
};
536513

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

crates/apollo_consensus/src/state_machine.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ pub(crate) struct StateMachine {
9090
prevote_quorum: HashSet<Round>,
9191
mixed_prevote_quorum: HashSet<Round>,
9292
mixed_precommit_quorum: HashSet<Round>,
93+
// Tracks the latest self votes for efficient rebroadcasts.
94+
last_self_prevote: Option<Vote>,
95+
last_self_precommit: Option<Vote>,
9396
}
9497

9598
impl StateMachine {
@@ -122,6 +125,8 @@ impl StateMachine {
122125
prevote_quorum: HashSet::new(),
123126
mixed_prevote_quorum: HashSet::new(),
124127
mixed_precommit_quorum: HashSet::new(),
128+
last_self_prevote: None,
129+
last_self_precommit: None,
125130
}
126131
}
127132

@@ -161,13 +166,40 @@ impl StateMachine {
161166
self.proposals.get(&round).and_then(|(id, _)| *id)
162167
}
163168

169+
pub(crate) fn last_self_prevote(&self) -> Option<Vote> {
170+
self.last_self_prevote.clone()
171+
}
172+
173+
pub(crate) fn last_self_precommit(&self) -> Option<Vote> {
174+
self.last_self_precommit.clone()
175+
}
176+
164177
fn make_self_vote(
165-
&self,
178+
&mut self,
166179
vote_type: VoteType,
167180
round: Round,
168181
proposal_commitment: Option<ProposalCommitment>,
169182
) -> Vote {
170-
Vote { vote_type, height: self.height.0, round, proposal_commitment, voter: self.id }
183+
let vt = vote_type.clone();
184+
let vote = Vote {
185+
vote_type: vt,
186+
height: self.height.0,
187+
round,
188+
proposal_commitment,
189+
voter: self.id,
190+
};
191+
// update the latest self vote.
192+
let last_self_vote = match vote_type {
193+
VoteType::Prevote => &mut self.last_self_prevote,
194+
VoteType::Precommit => &mut self.last_self_precommit,
195+
};
196+
if match last_self_vote {
197+
None => true,
198+
Some(last) => round > last.round,
199+
} {
200+
*last_self_vote = Some(vote.clone());
201+
}
202+
vote
171203
}
172204

173205
/// Starts the state machine, effectively calling `StartRound(0)` from the paper. This is

0 commit comments

Comments
 (0)