Skip to content

Commit cf78c42

Browse files
apollo_consensus: SM tracks last self votes not the SHC
1 parent 8089a74 commit cf78c42

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
));
@@ -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: 34 additions & 2 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,40 @@ 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,
176189
round: Round,
177190
proposal_commitment: Option<ProposalCommitment>,
178191
) -> Vote {
179-
Vote { vote_type, height: self.height.0, round, proposal_commitment, voter: self.id }
192+
let vt = vote_type.clone();
193+
let vote = Vote {
194+
vote_type: vt,
195+
height: self.height.0,
196+
round,
197+
proposal_commitment,
198+
voter: self.id,
199+
};
200+
// update the latest self vote.
201+
let last_self_vote = match vote_type {
202+
VoteType::Prevote => &mut self.last_self_prevote,
203+
VoteType::Precommit => &mut self.last_self_precommit,
204+
};
205+
if match last_self_vote {
206+
None => true,
207+
Some(last) => round > last.round,
208+
} {
209+
*last_self_vote = Some(vote.clone());
210+
}
211+
vote
180212
}
181213

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

0 commit comments

Comments
 (0)