Skip to content

Commit 81e03c6

Browse files
author
Hyunsik Jeong
committed
Wait in propose step when generated proposal is empty
Wait for (proposal timeout)/2. Empty proposals are generated immediately, so it would not cause a problem.
1 parent 8bd0a2d commit 81e03c6

File tree

3 files changed

+54
-2
lines changed

3 files changed

+54
-2
lines changed

core/src/block.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ impl LockedBlock {
340340
/// A block that has a valid seal.
341341
///
342342
/// The block's header has valid seal arguments. The block cannot be reversed into a `ClosedBlock` or `OpenBlock`.
343+
#[derive(Clone)]
343344
pub struct SealedBlock {
344345
block: ExecutedBlock,
345346
}

core/src/consensus/tendermint/mod.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ use crate::BlockId;
6363
use ChainNotify;
6464

6565
/// 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;
6769

6870
pub type BlockHash = H256;
6971

@@ -96,6 +98,8 @@ struct TendermintInner {
9698
last_lock: View,
9799
/// hash of the proposed block, used for seal submission.
98100
proposal: Option<H256>,
101+
/// Waiting proposal block, because it's empty
102+
waiting_proposal: Option<SealedBlock>,
99103
/// The last view of the previous height
100104
last_confirmed_view: View,
101105
/// Set used to determine the current validators.
@@ -154,6 +158,7 @@ impl TendermintInner {
154158
lock_change: None,
155159
last_lock: 0,
156160
proposal: None,
161+
waiting_proposal: None,
157162
last_confirmed_view: 0,
158163
validators: our_params.validators,
159164
block_reward: our_params.block_reward,
@@ -703,6 +708,21 @@ impl TendermintInner {
703708
panic!("Block is generated at unexpected step {:?}", self.step);
704709
}
705710

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+
706726
let vote_step =
707727
VoteStep::new(header.number() as Height, consensus_view(&header).expect("I am proposer"), Step::Propose);
708728
let vote_info = message_info_rlp(vote_step, Some(hash));
@@ -868,6 +888,23 @@ impl TendermintInner {
868888
}
869889

870890
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
871908
if self.extension().is_expired_timeout_token(token) {
872909
return
873910
}
@@ -889,6 +926,10 @@ impl TendermintInner {
889926
cwarn!(ENGINE, "Propose timed out but block is not generated yet");
890927
None
891928
}
929+
TendermintState::ProposeWaitEmpty => {
930+
cwarn!(ENGINE, "Propose timed out but still waiting for the empty block");
931+
None
932+
}
892933
TendermintState::Prevote if self.has_enough_any_votes() => {
893934
ctrace!(ENGINE, "Prevote timeout.");
894935
Some(Step::Precommit)
@@ -1545,12 +1586,20 @@ impl TendermintExtension {
15451586
fn set_timer_step(&self, step: Step, view: View) {
15461587
let expired_token_nonce = self.timeout_token_nonce.fetch_add(1, AtomicOrdering::SeqCst);
15471588

1589+
self.api.clear_timer(ENGINE_TIMEOUT_EMPTY_PROPOSAL).expect("Timer clear succeeds");
15481590
self.api.clear_timer(expired_token_nonce).expect("Timer clear succeeds");
15491591
self.api
15501592
.set_timer_once(expired_token_nonce + 1, self.timeouts.timeout(step, view))
15511593
.expect("Timer set succeeds");
15521594
}
15531595

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+
15541603
fn on_proposal_message(&self, tendermint: MutexGuard<TendermintInner>, signature: SchnorrSignature, bytes: Bytes) {
15551604
let c = match self.client.upgrade() {
15561605
Some(c) => c,
@@ -1803,7 +1852,7 @@ impl NetworkExtension for TendermintExtension {
18031852

18041853
impl TimeoutHandler for TendermintExtension {
18051854
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);
18071856
if let Some(c) = self.tendermint.upgrade() {
18081857
c.on_timeout(token);
18091858
}

core/src/consensus/tendermint/types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub enum TendermintState {
3232
ProposeWaitBlockGeneration {
3333
parent_hash: H256,
3434
},
35+
ProposeWaitEmpty,
3536
Prevote,
3637
Precommit,
3738
Commit,
@@ -44,6 +45,7 @@ impl TendermintState {
4445
TendermintState::ProposeWaitBlockGeneration {
4546
..
4647
} => Step::Propose,
48+
TendermintState::ProposeWaitEmpty => Step::Propose,
4749
TendermintState::Prevote => Step::Prevote,
4850
TendermintState::Precommit => Step::Precommit,
4951
TendermintState::Commit => Step::Commit,

0 commit comments

Comments
 (0)