diff --git a/core/src/consensus/tendermint/types.rs b/core/src/consensus/tendermint/types.rs index bdacb40906..7cf582ebfe 100644 --- a/core/src/consensus/tendermint/types.rs +++ b/core/src/consensus/tendermint/types.rs @@ -76,6 +76,15 @@ impl TendermintState { } } + pub fn is_propose_wait_empty_block_timer(&self) -> bool { + match self { + TendermintState::ProposeWaitEmptyBlockTimer { + .. + } => true, + _ => false, + } + } + pub fn is_commit(&self) -> bool { match self { TendermintState::Commit { diff --git a/core/src/consensus/tendermint/worker.rs b/core/src/consensus/tendermint/worker.rs index 45ceb5ad6b..04df0942bd 100644 --- a/core/src/consensus/tendermint/worker.rs +++ b/core/src/consensus/tendermint/worker.rs @@ -949,7 +949,9 @@ impl Worker { block, } => { if !block.transactions().is_empty() { - self.submit_proposal_block(&block); + cinfo!(ENGINE, "Submitting proposal block {}", block.header().hash()); + self.move_to_step(TendermintState::Prevote, false); + self.broadcast_proposal_block(self.view, encoded::Block::new(block.rlp_bytes())); } else { ctrace!(ENGINE, "Empty proposal is generated, set timer"); self.step = TendermintState::ProposeWaitEmptyBlockTimer { @@ -986,12 +988,6 @@ impl Worker { } } - fn submit_proposal_block(&mut self, sealed_block: &SealedBlock) { - cinfo!(ENGINE, "Submitting proposal block {}", sealed_block.header().hash()); - self.move_to_step(TendermintState::Prevote, false); - self.broadcast_proposal_block(self.view, encoded::Block::new(sealed_block.rlp_bytes())); - } - fn backup(&self) { backup(self.client().get_kvdb().as_ref(), BackupView { height: &self.height, @@ -1214,25 +1210,30 @@ impl Worker { fn on_timeout(&mut self, token: usize) { // Timeout from empty block generation if token == ENGINE_TIMEOUT_EMPTY_PROPOSAL { - let prev_step = mem::replace(&mut self.step, TendermintState::Propose); - match prev_step { - TendermintState::ProposeWaitEmptyBlockTimer { - block, - } => { - if self.height == block.header().number() { - cdebug!( - ENGINE, - "Empty proposal timer is finished, go to the prevote step and broadcast the block" - ); - self.submit_proposal_block(block.as_ref()); - } else { - cwarn!(ENGINE, "Empty proposal timer was for previous height."); - } - } - _ => { - cwarn!(ENGINE, "Empty proposal timer was not cleared."); + let block = if self.step.is_propose_wait_empty_block_timer() { + let previous = mem::replace(&mut self.step, TendermintState::Propose); + match previous { + TendermintState::ProposeWaitEmptyBlockTimer { + block, + } => block, + _ => unreachable!(), } + } else { + cwarn!(ENGINE, "Empty proposal timer was not cleared."); + return + }; + + // When self.height != block.header().number() && "propose timeout" is already called, + // the state is stuck and can't move to Prevote. We should change the step to Prevote. + self.move_to_step(TendermintState::Prevote, false); + if self.height == block.header().number() { + cdebug!(ENGINE, "Empty proposal timer is finished, go to the prevote step and broadcast the block"); + cinfo!(ENGINE, "Submitting proposal block {}", block.header().hash()); + self.broadcast_proposal_block(self.view, encoded::Block::new(block.rlp_bytes())); + } else { + cwarn!(ENGINE, "Empty proposal timer was for previous height."); } + return }