@@ -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