Skip to content

Commit 47870a4

Browse files
committed
Move to Commit state if enough precommits are collected
1 parent 536a076 commit 47870a4

File tree

2 files changed

+161
-41
lines changed

2 files changed

+161
-41
lines changed

core/src/consensus/tendermint/types.rs

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rlp::{Decodable, DecoderError, Encodable, RlpStream, UntrustedRlp};
2323
use super::super::BitSet;
2424
use super::message::VoteStep;
2525
use crate::block::{IsBlock, SealedBlock};
26+
use consensus::tendermint::BlockHash;
2627

2728
pub type Height = u64;
2829
pub type View = u64;
@@ -41,8 +42,14 @@ pub enum TendermintState {
4142
},
4243
Prevote,
4344
Precommit,
44-
Commit,
45-
CommitTimedout,
45+
Commit {
46+
view: View,
47+
block_hash: H256,
48+
},
49+
CommitTimedout {
50+
view: View,
51+
block_hash: H256,
52+
},
4653
}
4754

4855
impl TendermintState {
@@ -60,25 +67,60 @@ impl TendermintState {
6067
} => Step::Propose,
6168
TendermintState::Prevote => Step::Prevote,
6269
TendermintState::Precommit => Step::Precommit,
63-
TendermintState::Commit => Step::Commit,
64-
TendermintState::CommitTimedout => Step::Commit,
70+
TendermintState::Commit {
71+
..
72+
} => Step::Commit,
73+
TendermintState::CommitTimedout {
74+
..
75+
} => Step::Commit,
6576
}
6677
}
6778

6879
pub fn is_commit(&self) -> bool {
6980
match self {
70-
TendermintState::Commit => true,
71-
TendermintState::CommitTimedout => true,
81+
TendermintState::Commit {
82+
..
83+
} => true,
84+
TendermintState::CommitTimedout {
85+
..
86+
} => true,
7287
_ => false,
7388
}
7489
}
7590

7691
pub fn is_commit_timedout(&self) -> bool {
7792
match self {
78-
TendermintState::CommitTimedout => true,
93+
TendermintState::CommitTimedout {
94+
..
95+
} => true,
7996
_ => false,
8097
}
8198
}
99+
100+
pub fn committed(&self) -> Option<(View, BlockHash)> {
101+
match self {
102+
TendermintState::Commit {
103+
block_hash,
104+
view,
105+
} => Some((*view, *block_hash)),
106+
TendermintState::CommitTimedout {
107+
block_hash,
108+
view,
109+
} => Some((*view, *block_hash)),
110+
TendermintState::Propose => None,
111+
TendermintState::ProposeWaitBlockGeneration {
112+
..
113+
} => None,
114+
TendermintState::ProposeWaitImported {
115+
..
116+
} => None,
117+
TendermintState::ProposeWaitEmptyBlockTimer {
118+
..
119+
} => None,
120+
TendermintState::Prevote => None,
121+
TendermintState::Precommit => None,
122+
}
123+
}
82124
}
83125

84126
impl fmt::Debug for TendermintState {
@@ -96,8 +138,14 @@ impl fmt::Debug for TendermintState {
96138
} => write!(f, "TendermintState::ProposeWaitEmptyBlockTimer({})", block.header().hash()),
97139
TendermintState::Prevote => write!(f, "TendermintState::Prevote"),
98140
TendermintState::Precommit => write!(f, "TendermintState::Precommit"),
99-
TendermintState::Commit => write!(f, "TendermintState::Commit"),
100-
TendermintState::CommitTimedout => write!(f, "TendermintState::CommitTimedout"),
141+
TendermintState::Commit {
142+
block_hash,
143+
view,
144+
} => write!(f, "TendermintState::Commit({}, {})", block_hash, view),
145+
TendermintState::CommitTimedout {
146+
block_hash,
147+
view,
148+
} => write!(f, "TendermintState::CommitTimedout({}, {})", block_hash, view),
101149
}
102150
}
103151
}

core/src/consensus/tendermint/worker.rs

Lines changed: 104 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,10 @@ impl Worker {
405405

406406
/// Check Tendermint can move from the commit step to the propose step
407407
fn can_move_from_commit_to_propose(&self) -> bool {
408+
if !self.step.is_commit() {
409+
return false
410+
}
411+
408412
let vote_step = VoteStep::new(self.height, self.last_confirmed_view, Step::Precommit);
409413
if self.step.is_commit_timedout() && self.check_current_block_exists() {
410414
cinfo!(ENGINE, "Transition to Propose because best block is changed after commit timeout");
@@ -442,6 +446,20 @@ impl Worker {
442446
Some((proposal.signature, proposal.signer_index, bytes))
443447
}
444448

449+
fn is_proposal_received(&self, height: Height, view: View, block_hash: BlockHash) -> bool {
450+
let all_votes = self.votes.get_all_votes_in_round(&VoteStep {
451+
height,
452+
view,
453+
step: Step::Propose,
454+
});
455+
456+
if let Some(proposal) = all_votes.first() {
457+
proposal.on.block_hash.expect("Proposal message always include block hash") == block_hash
458+
} else {
459+
false
460+
}
461+
}
462+
445463
pub fn vote_step(&self) -> VoteStep {
446464
VoteStep {
447465
height: self.height,
@@ -592,6 +610,7 @@ impl Worker {
592610
self.votes_received = BitSet::new();
593611
}
594612

613+
#[allow(clippy::cognitive_complexity)]
595614
fn move_to_step(&mut self, state: TendermintState, is_restoring: bool) {
596615
ctrace!(ENGINE, "Transition to {:?} triggered.", state);
597616
let prev_step = mem::replace(&mut self.step, state.clone());
@@ -707,6 +726,34 @@ impl Worker {
707726
}
708727
Step::Commit => {
709728
cinfo!(ENGINE, "move_to_step: Commit.");
729+
let (view, block_hash) = state.committed().expect("commit always has committed_view");
730+
self.save_last_confirmed_view(view);
731+
732+
let proposal_received = self.is_proposal_received(self.height, view, block_hash);
733+
let proposal_imported = self.client().block(&block_hash.into()).is_some();
734+
let best_block_header = self.client().best_block_header();
735+
if best_block_header.number() >= self.height {
736+
cwarn!(
737+
ENGINE,
738+
"best_block_header.number() >= self.height ({} >= {}) in Commit state",
739+
best_block_header.number(),
740+
self.height
741+
);
742+
return
743+
}
744+
745+
let should_update_best_block = best_block_header.hash() != block_hash;
746+
747+
cdebug!(
748+
ENGINE,
749+
"commited, proposal_received: {}, proposal_imported: {}, should_update_best_block: {}",
750+
proposal_received,
751+
proposal_imported,
752+
should_update_best_block
753+
);
754+
if proposal_imported && should_update_best_block {
755+
self.client().update_best_as_committed(block_hash);
756+
}
710757
}
711758
}
712759
}
@@ -818,28 +865,39 @@ impl Worker {
818865
self.last_two_thirds_majority =
819866
TwoThirdsMajority::from_message(message.on.step.view, message.on.block_hash);
820867
}
868+
869+
if vote_step.step == Step::Precommit
870+
&& self.height == vote_step.height
871+
&& message.on.block_hash.is_some()
872+
&& has_enough_aligned_votes
873+
{
874+
if self.can_move_from_commit_to_propose() {
875+
let height = self.height;
876+
self.move_to_height(height + 1);
877+
self.move_to_step(TendermintState::Propose, is_restoring);
878+
return
879+
}
880+
881+
let block_hash = message.on.block_hash.expect("Upper if already checked block hash");
882+
if !self.step.is_commit() {
883+
self.move_to_step(
884+
TendermintState::Commit {
885+
block_hash,
886+
view: vote_step.view,
887+
},
888+
is_restoring,
889+
);
890+
return
891+
}
892+
}
893+
821894
// Check if it can affect the step transition.
822895
if self.is_step(message) {
823896
let next_step = match self.step {
824897
TendermintState::Precommit if message.on.block_hash.is_none() && has_enough_aligned_votes => {
825898
self.increment_view(1);
826899
Some(TendermintState::Propose)
827900
}
828-
TendermintState::Precommit if has_enough_aligned_votes => {
829-
let bh = message.on.block_hash.expect("previous guard ensures is_some; qed");
830-
if self.client().block(&BlockId::Hash(bh)).is_some() {
831-
// Commit the block, and update the last confirmed view
832-
self.save_last_confirmed_view(message.on.step.view);
833-
834-
// Update the best block hash as the hash of the committed block
835-
self.client().update_best_as_committed(bh);
836-
Some(TendermintState::Commit)
837-
} else {
838-
cwarn!(ENGINE, "Cannot find a proposal which committed");
839-
self.increment_view(1);
840-
Some(TendermintState::Propose)
841-
}
842-
}
843901
// Avoid counting votes twice.
844902
TendermintState::Prevote if lock_change => Some(TendermintState::Precommit),
845903
TendermintState::Prevote if has_enough_aligned_votes => Some(TendermintState::Precommit),
@@ -850,14 +908,6 @@ impl Worker {
850908
self.move_to_step(step, is_restoring);
851909
return
852910
}
853-
} else if vote_step.step == Step::Precommit
854-
&& self.height == vote_step.height
855-
&& self.can_move_from_commit_to_propose()
856-
{
857-
let height = self.height;
858-
self.move_to_height(height + 1);
859-
self.move_to_step(TendermintState::Propose, is_restoring);
860-
return
861911
}
862912

863913
// self.move_to_step() calls self.broadcast_state()
@@ -1227,18 +1277,27 @@ impl Worker {
12271277
cinfo!(ENGINE, "Precommit timeout without enough votes.");
12281278
TendermintState::Precommit
12291279
}
1230-
TendermintState::Commit => {
1280+
TendermintState::Commit {
1281+
block_hash,
1282+
view,
1283+
} => {
12311284
cinfo!(ENGINE, "Commit timeout.");
1232-
if !self.check_current_block_exists() {
1285+
if self.client().block(&block_hash.into()).is_none() {
12331286
cwarn!(ENGINE, "Best chain is not updated yet, wait until imported");
1234-
self.step = TendermintState::CommitTimedout;
1287+
self.step = TendermintState::CommitTimedout {
1288+
block_hash,
1289+
view,
1290+
};
12351291
return
12361292
}
1293+
12371294
let height = self.height;
12381295
self.move_to_height(height + 1);
12391296
TendermintState::Propose
12401297
}
1241-
TendermintState::CommitTimedout => unreachable!(),
1298+
TendermintState::CommitTimedout {
1299+
..
1300+
} => unreachable!(),
12421301
};
12431302

12441303
self.move_to_step(next_step, false);
@@ -1439,6 +1498,24 @@ impl Worker {
14391498
}
14401499
};
14411500

1501+
if self.step.is_commit() && (imported.len() + enacted.len() == 1) {
1502+
let (_, committed_block_hash) = self.step.committed().expect("Commit state always has block_hash");
1503+
if imported.first() == Some(&committed_block_hash) {
1504+
cdebug!(ENGINE, "Committed block {} is committed_block_hash", committed_block_hash);
1505+
self.client().update_best_as_committed(committed_block_hash);
1506+
return
1507+
}
1508+
if enacted.first() == Some(&committed_block_hash) {
1509+
cdebug!(ENGINE, "Committed block {} is now the best block", committed_block_hash);
1510+
if self.can_move_from_commit_to_propose() {
1511+
let new_height = self.height + 1;
1512+
self.move_to_height(new_height);
1513+
self.move_to_step(TendermintState::Propose, false);
1514+
return
1515+
}
1516+
}
1517+
}
1518+
14421519
if !imported.is_empty() {
14431520
let mut height_changed = false;
14441521
for hash in imported {
@@ -1463,11 +1540,6 @@ impl Worker {
14631540
return
14641541
}
14651542
}
1466-
if !enacted.is_empty() && self.can_move_from_commit_to_propose() {
1467-
let new_height = self.height + 1;
1468-
self.move_to_height(new_height);
1469-
self.move_to_step(TendermintState::Propose, false)
1470-
}
14711543
}
14721544

14731545
fn send_proposal_block(

0 commit comments

Comments
 (0)