@@ -63,7 +63,9 @@ use crate::BlockId;
63
63
use ChainNotify ;
64
64
65
65
/// Timer token representing the consensus step timeouts.
66
- pub const ENGINE_TIMEOUT_TOKEN_NONCE_BASE : TimerToken = 23 ;
66
+ const ENGINE_TIMEOUT_TOKEN_NONCE_BASE : TimerToken = 23 ;
67
+ /// Timer token for empty proposal blocks.
68
+ const ENGINE_TIMEOUT_EMPTY_PROPOSAL : TimerToken = 22 ;
67
69
68
70
pub type BlockHash = H256 ;
69
71
@@ -198,7 +200,7 @@ impl TendermintInner {
198
200
/// Check Tendermint can move from the commit step to the propose step
199
201
fn can_move_from_commit_to_propose ( & self ) -> bool {
200
202
let vote_step = VoteStep :: new ( self . height - 1 , self . last_confirmed_view , Step :: Precommit ) ;
201
- self . step == TendermintState :: Commit && self . has_all_votes ( & vote_step) && self . check_prev_block_exists ( )
203
+ self . step . is_commit ( ) && self . has_all_votes ( & vote_step) && self . check_prev_block_exists ( )
202
204
}
203
205
204
206
/// Find the designated for the given view.
@@ -373,7 +375,7 @@ impl TendermintInner {
373
375
// move_to_step can be called with the same step
374
376
// Also, when moving to the commit step,
375
377
// keep `votes_received` for gossiping.
376
- if prev_step != step. into ( ) && step != Step :: Commit {
378
+ if prev_step. to_step ( ) != step && step != Step :: Commit {
377
379
self . votes_received = BitSet :: new ( ) ;
378
380
}
379
381
@@ -555,7 +557,7 @@ impl TendermintInner {
555
557
let view = consensus_view ( proposal) . expect ( "Imported block is already verified" ) ;
556
558
if current_height == height && self . view == view {
557
559
self . proposal = Some ( proposal. hash ( ) ) ;
558
- if self . step == TendermintState :: Propose {
560
+ if self . step . is_propose ( ) {
559
561
self . move_to_step ( Step :: Prevote ) ;
560
562
}
561
563
} else if current_height < height {
@@ -703,6 +705,22 @@ impl TendermintInner {
703
705
panic ! ( "Block is generated at unexpected step {:?}" , self . step) ;
704
706
}
705
707
708
+ if !sealed_block. transactions ( ) . is_empty ( ) {
709
+ cdebug ! ( ENGINE , "Submit proposal {} immediatly" , hash) ;
710
+ self . submit_proposal_block ( sealed_block) ;
711
+ } else {
712
+ cdebug ! ( ENGINE , "Submit proposal {} after the half of the proposal timeout, because it's empty" , hash) ;
713
+ self . step = TendermintState :: ProposeWaitEmpty {
714
+ block : Box :: new ( sealed_block. clone ( ) ) ,
715
+ } ;
716
+ self . extension ( ) . set_timer_empty_proposal ( self . view ) ;
717
+ }
718
+ }
719
+
720
+ fn submit_proposal_block ( & mut self , sealed_block : & SealedBlock ) {
721
+ let header = sealed_block. header ( ) ;
722
+ let hash = header. hash ( ) ;
723
+
706
724
let vote_step =
707
725
VoteStep :: new ( header. number ( ) as Height , consensus_view ( & header) . expect ( "I am proposer" ) , Step :: Propose ) ;
708
726
let vote_info = message_info_rlp ( vote_step, Some ( hash) ) ;
@@ -864,6 +882,23 @@ impl TendermintInner {
864
882
}
865
883
866
884
fn on_timeout ( & mut self , token : usize ) {
885
+ // Timeout from empty block generation
886
+ if token == ENGINE_TIMEOUT_EMPTY_PROPOSAL {
887
+ let prev_step = mem:: replace ( & mut self . step , TendermintState :: Propose ) ;
888
+ match prev_step {
889
+ TendermintState :: ProposeWaitEmpty {
890
+ block,
891
+ } => {
892
+ self . submit_proposal_block ( & block) ;
893
+ }
894
+ _ => {
895
+ cwarn ! ( ENGINE , "Empty proposal timer was not cleared." ) ;
896
+ }
897
+ }
898
+ return
899
+ }
900
+
901
+ // Timeout from Tendermint step
867
902
if self . extension ( ) . is_expired_timeout_token ( token) {
868
903
return
869
904
}
@@ -885,6 +920,12 @@ impl TendermintInner {
885
920
cwarn ! ( ENGINE , "Propose timed out but block is not generated yet" ) ;
886
921
None
887
922
}
923
+ TendermintState :: ProposeWaitEmpty {
924
+ ..
925
+ } => {
926
+ cwarn ! ( ENGINE , "Propose timed out but still waiting for the empty block" ) ;
927
+ Some ( Step :: Prevote )
928
+ }
888
929
TendermintState :: Prevote if self . has_enough_any_votes ( ) => {
889
930
ctrace ! ( ENGINE , "Prevote timeout." ) ;
890
931
Some ( Step :: Precommit )
@@ -1536,12 +1577,20 @@ impl TendermintExtension {
1536
1577
fn set_timer_step ( & self , step : Step , view : View ) {
1537
1578
let expired_token_nonce = self . timeout_token_nonce . fetch_add ( 1 , AtomicOrdering :: SeqCst ) ;
1538
1579
1580
+ self . api . clear_timer ( ENGINE_TIMEOUT_EMPTY_PROPOSAL ) . expect ( "Timer clear succeeds" ) ;
1539
1581
self . api . clear_timer ( expired_token_nonce) . expect ( "Timer clear succeeds" ) ;
1540
1582
self . api
1541
1583
. set_timer_once ( expired_token_nonce + 1 , self . timeouts . timeout ( step, view) )
1542
1584
. expect ( "Timer set succeeds" ) ;
1543
1585
}
1544
1586
1587
+ fn set_timer_empty_proposal ( & self , view : View ) {
1588
+ self . api . clear_timer ( ENGINE_TIMEOUT_EMPTY_PROPOSAL ) . expect ( "Timer clear succeeds" ) ;
1589
+ self . api
1590
+ . set_timer_once ( ENGINE_TIMEOUT_EMPTY_PROPOSAL , self . timeouts . timeout ( Step :: Propose , view) / 2 )
1591
+ . expect ( "Timer set succeeds" ) ;
1592
+ }
1593
+
1545
1594
fn on_proposal_message ( & self , tendermint : MutexGuard < TendermintInner > , signature : SchnorrSignature , bytes : Bytes ) {
1546
1595
let c = match self . client . upgrade ( ) {
1547
1596
Some ( c) => c,
@@ -1640,7 +1689,7 @@ impl TendermintExtension {
1640
1689
) ;
1641
1690
self . update_peer_state ( token, peer_vote_step, peer_proposal, peer_known_votes) ;
1642
1691
1643
- let current_vote_step = if tendermint. step == TendermintState :: Commit {
1692
+ let current_vote_step = if tendermint. step . is_commit ( ) {
1644
1693
// Even in the commit step, it must be possible to get pre-commits from
1645
1694
// the previous step. So, act as the last precommit step.
1646
1695
VoteStep {
@@ -1794,7 +1843,7 @@ impl NetworkExtension for TendermintExtension {
1794
1843
1795
1844
impl TimeoutHandler for TendermintExtension {
1796
1845
fn on_timeout ( & self , token : TimerToken ) {
1797
- debug_assert ! ( token >= ENGINE_TIMEOUT_TOKEN_NONCE_BASE ) ;
1846
+ debug_assert ! ( token >= ENGINE_TIMEOUT_TOKEN_NONCE_BASE || token == ENGINE_TIMEOUT_EMPTY_PROPOSAL ) ;
1798
1847
if let Some ( c) = self . tendermint . upgrade ( ) {
1799
1848
c. on_timeout ( token) ;
1800
1849
}
0 commit comments