@@ -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,47 @@ impl Worker {
707726 }
708727 Step :: Commit => {
709728 cinfo ! ( ENGINE , "move_to_step: Commit." ) ;
729+ let view = state. committed_view ( ) . expect ( "commit always has committed_view" ) ;
730+ let block_hash = state. committed_block_hash ( ) . expect ( "commit always has committed_block_hash" ) ;
731+ self . save_last_confirmed_view ( view) ;
732+
733+ let proposal_received = self . is_proposal_received ( self . height , view, block_hash) ;
734+ let proposal_imported = self . client ( ) . block ( & block_hash. into ( ) ) . is_some ( ) ;
735+ let best_block_header = self . client ( ) . best_block_header ( ) ;
736+ if best_block_header. number ( ) > self . height {
737+ cwarn ! (
738+ ENGINE ,
739+ "Best block's number {} is larger than tendermint height {} in Commit state" ,
740+ best_block_header. number( ) ,
741+ self . height
742+ ) ;
743+ return
744+ } else if best_block_header. number ( ) == self . height {
745+ cwarn ! (
746+ ENGINE ,
747+ "Best block's number {} is the same as the tendermint height {} in Commit state" ,
748+ best_block_header. number( ) ,
749+ self . height
750+ ) ;
751+ return
752+ }
753+ let best_block_changed = best_block_header. hash ( ) == block_hash;
754+
755+ match ( proposal_received, proposal_imported, best_block_changed) {
756+ ( false , false , _) => {
757+ cdebug ! ( ENGINE , "committed but proposal({}) is not received wait the proposal" , block_hash) ;
758+ }
759+ ( true , false , _) => {
760+ cdebug ! ( ENGINE , "committed but proposal({}) is not imported wait the proposal" , block_hash) ;
761+ }
762+ ( _, true , false ) => {
763+ cdebug ! ( ENGINE , "committed but proposal({}) is imported update the best block" , block_hash) ;
764+ self . client ( ) . update_best_as_committed ( block_hash) ;
765+ }
766+ ( _, true , true ) => {
767+ cdebug ! ( ENGINE , "committed proposal({}) is imported and best is changed" , block_hash) ;
768+ }
769+ }
710770 }
711771 }
712772 }
@@ -818,28 +878,39 @@ impl Worker {
818878 self . last_two_thirds_majority =
819879 TwoThirdsMajority :: from_message ( message. on . step . view , message. on . block_hash ) ;
820880 }
881+
882+ if vote_step. step == Step :: Precommit
883+ && self . height == vote_step. height
884+ && message. on . block_hash . is_some ( )
885+ && has_enough_aligned_votes
886+ {
887+ if self . can_move_from_commit_to_propose ( ) {
888+ let height = self . height ;
889+ self . move_to_height ( height + 1 ) ;
890+ self . move_to_step ( TendermintState :: Propose , is_restoring) ;
891+ return
892+ }
893+
894+ let block_hash = message. on . block_hash . expect ( "Upper if already checked block hash" ) ;
895+ if !self . step . is_commit ( ) {
896+ self . move_to_step (
897+ TendermintState :: Commit {
898+ block_hash,
899+ view : vote_step. view ,
900+ } ,
901+ is_restoring,
902+ ) ;
903+ return
904+ }
905+ }
906+
821907 // Check if it can affect the step transition.
822908 if self . is_step ( message) {
823909 let next_step = match self . step {
824910 TendermintState :: Precommit if message. on . block_hash . is_none ( ) && has_enough_aligned_votes => {
825911 self . increment_view ( 1 ) ;
826912 Some ( TendermintState :: Propose )
827913 }
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- }
843914 // Avoid counting votes twice.
844915 TendermintState :: Prevote if lock_change => Some ( TendermintState :: Precommit ) ,
845916 TendermintState :: Prevote if has_enough_aligned_votes => Some ( TendermintState :: Precommit ) ,
@@ -850,14 +921,6 @@ impl Worker {
850921 self . move_to_step ( step, is_restoring) ;
851922 return
852923 }
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
861924 }
862925
863926 // self.move_to_step() calls self.broadcast_state()
@@ -1227,18 +1290,27 @@ impl Worker {
12271290 cinfo ! ( ENGINE , "Precommit timeout without enough votes." ) ;
12281291 TendermintState :: Precommit
12291292 }
1230- TendermintState :: Commit => {
1293+ TendermintState :: Commit {
1294+ block_hash,
1295+ view,
1296+ } => {
12311297 cinfo ! ( ENGINE , "Commit timeout." ) ;
1232- if ! self . check_current_block_exists ( ) {
1298+ if self . client ( ) . block ( & block_hash . into ( ) ) . is_none ( ) {
12331299 cwarn ! ( ENGINE , "Best chain is not updated yet, wait until imported" ) ;
1234- self . step = TendermintState :: CommitTimedout ;
1300+ self . step = TendermintState :: CommitTimedout {
1301+ block_hash,
1302+ view,
1303+ } ;
12351304 return
12361305 }
1306+
12371307 let height = self . height ;
12381308 self . move_to_height ( height + 1 ) ;
12391309 TendermintState :: Propose
12401310 }
1241- TendermintState :: CommitTimedout => unreachable ! ( ) ,
1311+ TendermintState :: CommitTimedout {
1312+ ..
1313+ } => unreachable ! ( ) ,
12421314 } ;
12431315
12441316 self . move_to_step ( next_step, false ) ;
@@ -1439,6 +1511,24 @@ impl Worker {
14391511 }
14401512 } ;
14411513
1514+ if self . step . is_commit ( ) && ( imported. len ( ) + enacted. len ( ) == 1 ) {
1515+ let committed_block_hash = self . step . committed_block_hash ( ) . expect ( "Commit state always has block_hash" ) ;
1516+ if imported. first ( ) == Some ( & committed_block_hash) {
1517+ cdebug ! ( ENGINE , "Committed block {} is committed_block_hash" , committed_block_hash) ;
1518+ self . client ( ) . update_best_as_committed ( committed_block_hash) ;
1519+ return
1520+ }
1521+ if enacted. first ( ) == Some ( & committed_block_hash) {
1522+ cdebug ! ( ENGINE , "Committed block {} is now the best block" , committed_block_hash) ;
1523+ if self . can_move_from_commit_to_propose ( ) {
1524+ let new_height = self . height + 1 ;
1525+ self . move_to_height ( new_height) ;
1526+ self . move_to_step ( TendermintState :: Propose , false ) ;
1527+ return
1528+ }
1529+ }
1530+ }
1531+
14421532 if !imported. is_empty ( ) {
14431533 let mut height_changed = false ;
14441534 for hash in imported {
@@ -1463,11 +1553,6 @@ impl Worker {
14631553 return
14641554 }
14651555 }
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- }
14711556 }
14721557
14731558 fn send_proposal_block (
0 commit comments