Skip to content

Commit ea47287

Browse files
apollo_consensus: SM tracks last self votes not the SHC
1 parent 10cf82e commit ea47287

File tree

2 files changed

+48
-42
lines changed

2 files changed

+48
-42
lines changed

crates/apollo_consensus/src/single_height_consensus.rs

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,6 @@ pub(crate) struct SingleHeightConsensus {
137137
state_machine: StateMachine,
138138
// Tracks rounds for which we started validating a proposal to avoid duplicate validations.
139139
pending_validation_rounds: HashSet<Round>,
140-
last_prevote: Option<Vote>,
141-
last_precommit: Option<Vote>,
142140
}
143141

144142
impl SingleHeightConsensus {
@@ -154,14 +152,7 @@ impl SingleHeightConsensus {
154152
let n_validators =
155153
u64::try_from(validators.len()).expect("Should have way less than u64::MAX validators");
156154
let state_machine = StateMachine::new(height, id, n_validators, is_observer, quorum_type);
157-
Self {
158-
validators,
159-
timeouts,
160-
state_machine,
161-
pending_validation_rounds: HashSet::new(),
162-
last_prevote: None,
163-
last_precommit: None,
164-
}
155+
Self { validators, timeouts, state_machine, pending_validation_rounds: HashSet::new() }
165156
}
166157

167158
pub(crate) fn current_round(&self) -> Round {
@@ -242,7 +233,7 @@ impl SingleHeightConsensus {
242233
self.handle_timeout(context, event).await
243234
}
244235
StateMachineEvent::Prevote(vote) => {
245-
let Some(last_vote) = &self.last_prevote else {
236+
let Some(last_vote) = self.state_machine.last_self_prevote() else {
246237
return Err(ConsensusError::InternalInconsistency(
247238
"No prevote to send".to_string(),
248239
));
@@ -252,14 +243,14 @@ impl SingleHeightConsensus {
252243
return Ok(ShcReturn::Tasks(Vec::new()));
253244
}
254245
trace_every_n_sec!(REBROADCAST_LOG_PERIOD_SECS, "Rebroadcasting {last_vote:?}");
255-
context.broadcast(last_vote.clone()).await?;
246+
context.broadcast(last_vote).await?;
256247
Ok(ShcReturn::Tasks(vec![ShcTask::Prevote(
257248
self.timeouts.prevote_timeout,
258249
StateMachineEvent::Prevote(vote),
259250
)]))
260251
}
261252
StateMachineEvent::Precommit(vote) => {
262-
let Some(last_vote) = &self.last_precommit else {
253+
let Some(last_vote) = self.state_machine.last_self_precommit() else {
263254
return Err(ConsensusError::InternalInconsistency(
264255
"No precommit to send".to_string(),
265256
));
@@ -269,7 +260,7 @@ impl SingleHeightConsensus {
269260
return Ok(ShcReturn::Tasks(Vec::new()));
270261
}
271262
trace_every_n_sec!(REBROADCAST_LOG_PERIOD_SECS, "Rebroadcasting {last_vote:?}");
272-
context.broadcast(last_vote.clone()).await?;
263+
context.broadcast(last_vote).await?;
273264
Ok(ShcReturn::Tasks(vec![ShcTask::Precommit(
274265
self.timeouts.precommit_timeout,
275266
StateMachineEvent::Precommit(vote),
@@ -488,37 +479,18 @@ impl SingleHeightConsensus {
488479
context: &mut ContextT,
489480
vote: Vote,
490481
) -> Result<Vec<ShcTask>, ConsensusError> {
491-
let (last_vote, task) = match vote.vote_type {
492-
VoteType::Prevote => (
493-
&mut self.last_prevote,
494-
ShcTask::Prevote(
495-
self.timeouts.prevote_timeout,
496-
StateMachineEvent::Prevote(vote.clone()),
497-
),
482+
let task = match vote.vote_type {
483+
VoteType::Prevote => ShcTask::Prevote(
484+
self.timeouts.prevote_timeout,
485+
StateMachineEvent::Prevote(vote.clone()),
498486
),
499-
VoteType::Precommit => (
500-
&mut self.last_precommit,
501-
ShcTask::Precommit(
502-
self.timeouts.precommit_timeout,
503-
StateMachineEvent::Precommit(vote.clone()),
504-
),
487+
VoteType::Precommit => ShcTask::Precommit(
488+
self.timeouts.precommit_timeout,
489+
StateMachineEvent::Precommit(vote.clone()),
505490
),
506491
};
507492
// Ensure the voter matches this node.
508493
assert_eq!(vote.voter, self.state_machine.id());
509-
*last_vote = match last_vote {
510-
None => Some(vote.clone()),
511-
Some(last_vote) if vote.round > last_vote.round => Some(vote.clone()),
512-
Some(_) => {
513-
// According to the Tendermint paper, the state machine should only vote for its
514-
// current round. It should monotonically increase its round. It should only vote
515-
// once per step.
516-
return Err(ConsensusError::InternalInconsistency(format!(
517-
"State machine must progress in time: last_vote: {last_vote:?} new_vote: \
518-
{vote:?}",
519-
)));
520-
}
521-
};
522494

523495
info!("Broadcasting {vote:?}");
524496
context.broadcast(vote).await?;

crates/apollo_consensus/src/state_machine.rs

Lines changed: 36 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

@@ -159,13 +164,42 @@ impl StateMachine {
159164
&self.proposals
160165
}
161166

167+
pub(crate) fn last_self_prevote(&self) -> Option<Vote> {
168+
self.last_self_prevote.clone()
169+
}
170+
171+
pub(crate) fn last_self_precommit(&self) -> Option<Vote> {
172+
self.last_self_precommit.clone()
173+
}
174+
162175
fn make_self_vote(
163-
&self,
176+
&mut self,
164177
vote_type: VoteType,
165178
round: Round,
166179
proposal_commitment: Option<ProposalCommitment>,
167180
) -> Vote {
168-
Vote { vote_type, height: self.height.0, round, proposal_commitment, voter: self.id }
181+
let vt = vote_type.clone();
182+
let vote = Vote {
183+
vote_type: vt,
184+
height: self.height.0,
185+
round,
186+
proposal_commitment,
187+
voter: self.id,
188+
};
189+
// update the latest self vote.
190+
match vote_type {
191+
VoteType::Prevote => match &self.last_self_prevote {
192+
None => self.last_self_prevote = Some(vote.clone()),
193+
Some(last) if round > last.round => self.last_self_prevote = Some(vote.clone()),
194+
_ => {}
195+
},
196+
VoteType::Precommit => match &self.last_self_precommit {
197+
None => self.last_self_precommit = Some(vote.clone()),
198+
Some(last) if round > last.round => self.last_self_precommit = Some(vote.clone()),
199+
_ => {}
200+
},
201+
}
202+
vote
169203
}
170204

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

0 commit comments

Comments
 (0)