Skip to content

Commit cc6dbdb

Browse files
apollo_consensus: SM tracks last self votes not the SHC
1 parent c1e36f6 commit cc6dbdb

File tree

3 files changed

+46
-42
lines changed

3 files changed

+46
-42
lines changed

crates/apollo_consensus/src/single_height_consensus.rs

Lines changed: 10 additions & 38 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
));
@@ -259,7 +250,7 @@ impl SingleHeightConsensus {
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
));
@@ -497,37 +488,18 @@ impl SingleHeightConsensus {
497488
context: &mut ContextT,
498489
vote: Vote,
499490
) -> Result<Vec<ShcTask>, ConsensusError> {
500-
let (last_vote, task) = match vote.vote_type {
501-
VoteType::Prevote => (
502-
&mut self.last_prevote,
503-
ShcTask::Prevote(
504-
self.timeouts.get_prevote_timeout(0),
505-
StateMachineEvent::Prevote(vote.clone()),
506-
),
491+
let task = match vote.vote_type {
492+
VoteType::Prevote => ShcTask::Prevote(
493+
self.timeouts.get_prevote_timeout(0),
494+
StateMachineEvent::Prevote(vote.clone()),
507495
),
508-
VoteType::Precommit => (
509-
&mut self.last_precommit,
510-
ShcTask::Precommit(
511-
self.timeouts.get_precommit_timeout(0),
512-
StateMachineEvent::Precommit(vote.clone()),
513-
),
496+
VoteType::Precommit => ShcTask::Precommit(
497+
self.timeouts.get_precommit_timeout(0),
498+
StateMachineEvent::Precommit(vote.clone()),
514499
),
515500
};
516501
// Ensure the voter matches this node.
517502
assert_eq!(vote.voter, self.state_machine.validator_id());
518-
*last_vote = match last_vote {
519-
None => Some(vote.clone()),
520-
Some(last_vote) if vote.round > last_vote.round => Some(vote.clone()),
521-
Some(_) => {
522-
// According to the Tendermint paper, the state machine should only vote for its
523-
// current round. It should monotonically increase its round. It should only vote
524-
// once per step.
525-
return Err(ConsensusError::InternalInconsistency(format!(
526-
"State machine must progress in time: last_vote: {last_vote:?} new_vote: \
527-
{vote:?}",
528-
)));
529-
}
530-
};
531503

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

crates/apollo_consensus/src/single_height_consensus_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ fn get_proposal_init_for_height(height: BlockNumber) -> ProposalInit {
4343
}
4444

4545
fn prevote_task(block_felt: Option<Felt>, round: u32, voter: ValidatorId) -> ShcTask {
46-
let duration = TIMEOUTS.get_prevote_timeout(round);
46+
let duration = TIMEOUTS.get_prevote_timeout(0);
4747
ShcTask::Prevote(duration, StateMachineEvent::Prevote(prevote(block_felt, 0, round, voter)))
4848
}
4949

5050
fn precommit_task(block_felt: Option<Felt>, round: u32, voter: ValidatorId) -> ShcTask {
51-
let duration = TIMEOUTS.get_precommit_timeout(round);
51+
let duration = TIMEOUTS.get_precommit_timeout(0);
5252
ShcTask::Precommit(
5353
duration,
5454
StateMachineEvent::Precommit(precommit(block_felt, 0, round, voter)),

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)