@@ -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
@@ -96,6 +98,8 @@ struct TendermintInner {
96
98
last_lock : View ,
97
99
/// hash of the proposed block, used for seal submission.
98
100
proposal : Option < H256 > ,
101
+ /// Waiting proposal block, because it's empty
102
+ waiting_proposal : Option < SealedBlock > ,
99
103
/// The last view of the previous height
100
104
last_confirmed_view : View ,
101
105
/// Set used to determine the current validators.
@@ -154,6 +158,7 @@ impl TendermintInner {
154
158
lock_change : None ,
155
159
last_lock : 0 ,
156
160
proposal : None ,
161
+ waiting_proposal : None ,
157
162
last_confirmed_view : 0 ,
158
163
validators : our_params. validators ,
159
164
block_reward : our_params. block_reward ,
@@ -703,6 +708,21 @@ impl TendermintInner {
703
708
panic ! ( "Block is generated at unexpected step {:?}" , self . step) ;
704
709
}
705
710
711
+ if !sealed_block. transactions ( ) . is_empty ( ) {
712
+ cdebug ! ( ENGINE , "Submit proposal {} immediatly" , hash) ;
713
+ self . submit_proposal_block ( sealed_block) ;
714
+ } else {
715
+ cdebug ! ( ENGINE , "Submit proposal {} after the half of the proposal timeout, because it's empty" , hash) ;
716
+ self . step = TendermintState :: ProposeWaitEmpty ;
717
+ self . waiting_proposal = Some ( sealed_block. clone ( ) ) ;
718
+ self . extension ( ) . set_timer_empty_proposal ( self . view ) ;
719
+ }
720
+ }
721
+
722
+ fn submit_proposal_block ( & mut self , sealed_block : & SealedBlock ) {
723
+ let header = sealed_block. header ( ) ;
724
+ let hash = header. hash ( ) ;
725
+
706
726
let vote_step =
707
727
VoteStep :: new ( header. number ( ) as Height , consensus_view ( & header) . expect ( "I am proposer" ) , Step :: Propose ) ;
708
728
let vote_info = message_info_rlp ( vote_step, Some ( hash) ) ;
@@ -868,6 +888,23 @@ impl TendermintInner {
868
888
}
869
889
870
890
fn on_timeout ( & mut self , token : usize ) {
891
+ // Timeout from empty block generation
892
+ if token == ENGINE_TIMEOUT_EMPTY_PROPOSAL {
893
+ match self . step {
894
+ TendermintState :: ProposeWaitEmpty => {
895
+ let block = mem:: replace ( & mut self . waiting_proposal , None )
896
+ . expect ( "waiting_proposal must not be None when in the ProposeWaitEmpty state" ) ;
897
+ self . submit_proposal_block ( & block) ;
898
+ }
899
+ _ => {
900
+ self . waiting_proposal = None ;
901
+ cwarn ! ( ENGINE , "Empty proposal timer was not cleared." ) ;
902
+ }
903
+ }
904
+ return
905
+ }
906
+
907
+ // Timeout from Tendermint step
871
908
if self . extension ( ) . is_expired_timeout_token ( token) {
872
909
return
873
910
}
@@ -889,6 +926,10 @@ impl TendermintInner {
889
926
cwarn ! ( ENGINE , "Propose timed out but block is not generated yet" ) ;
890
927
None
891
928
}
929
+ TendermintState :: ProposeWaitEmpty => {
930
+ cwarn ! ( ENGINE , "Propose timed out but still waiting for the empty block" ) ;
931
+ None
932
+ }
892
933
TendermintState :: Prevote if self . has_enough_any_votes ( ) => {
893
934
ctrace ! ( ENGINE , "Prevote timeout." ) ;
894
935
Some ( Step :: Precommit )
@@ -1545,12 +1586,20 @@ impl TendermintExtension {
1545
1586
fn set_timer_step ( & self , step : Step , view : View ) {
1546
1587
let expired_token_nonce = self . timeout_token_nonce . fetch_add ( 1 , AtomicOrdering :: SeqCst ) ;
1547
1588
1589
+ self . api . clear_timer ( ENGINE_TIMEOUT_EMPTY_PROPOSAL ) . expect ( "Timer clear succeeds" ) ;
1548
1590
self . api . clear_timer ( expired_token_nonce) . expect ( "Timer clear succeeds" ) ;
1549
1591
self . api
1550
1592
. set_timer_once ( expired_token_nonce + 1 , self . timeouts . timeout ( step, view) )
1551
1593
. expect ( "Timer set succeeds" ) ;
1552
1594
}
1553
1595
1596
+ fn set_timer_empty_proposal ( & self , view : View ) {
1597
+ self . api . clear_timer ( ENGINE_TIMEOUT_EMPTY_PROPOSAL ) . expect ( "Timer clear succeeds" ) ;
1598
+ self . api
1599
+ . set_timer_once ( ENGINE_TIMEOUT_EMPTY_PROPOSAL , self . timeouts . timeout ( Step :: Propose , view) / 2 )
1600
+ . expect ( "Timer set succeeds" ) ;
1601
+ }
1602
+
1554
1603
fn on_proposal_message ( & self , tendermint : MutexGuard < TendermintInner > , signature : SchnorrSignature , bytes : Bytes ) {
1555
1604
let c = match self . client . upgrade ( ) {
1556
1605
Some ( c) => c,
@@ -1803,7 +1852,7 @@ impl NetworkExtension for TendermintExtension {
1803
1852
1804
1853
impl TimeoutHandler for TendermintExtension {
1805
1854
fn on_timeout ( & self , token : TimerToken ) {
1806
- debug_assert ! ( token >= ENGINE_TIMEOUT_TOKEN_NONCE_BASE ) ;
1855
+ debug_assert ! ( token >= ENGINE_TIMEOUT_TOKEN_NONCE_BASE || token == ENGINE_TIMEOUT_EMPTY_PROPOSAL ) ;
1807
1856
if let Some ( c) = self . tendermint . upgrade ( ) {
1808
1857
c. on_timeout ( token) ;
1809
1858
}
0 commit comments