From c4294bd4d724e2366866aec4431f16fbe486f26b Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Sep 2019 16:38:37 +0400 Subject: [PATCH] cs: check for SkipTimeoutCommit or wait timeout in handleTxsAvailable (#3928) * cs: check for SkipTimeoutCommit or wait timeout in handleTxsAvailable Previously, if create_empty_blocks was set to false, TM sometimes could create 2 consecutive blocks without actually waiting for timeoutCommit (1 sec. by default). ``` I[2019-08-29|07:32:43.874] Executed block module=state height=47 validTxs=10 invalidTxs=0 I[2019-08-29|07:32:43.876] Committed state module=state height=47 txs=10 appHash=F88C010000000000 I[2019-08-29|07:32:44.885] Executed block module=state height=48 validTxs=2 invalidTxs=0 I[2019-08-29|07:32:44.887] Committed state module=state height=48 txs=2 appHash=FC8C010000000000 I[2019-08-29|07:32:44.908] Executed block module=state height=49 validTxs=8 invalidTxs=0 I[2019-08-29|07:32:44.909] Committed state module=state height=49 txs=8 appHash=8C8D010000000000 I[2019-08-29|07:32:45.886] Executed block module=state height=50 validTxs=2 invalidTxs=0 I[2019-08-29|07:32:45.895] Committed state module=state height=50 txs=2 appHash=908D010000000000 I[2019-08-29|07:32:45.908] Executed block module=state height=51 validTxs=8 invalidTxs=0 I[2019-08-29|07:32:45.909] Committed state module=state height=51 txs=8 appHash=A08D010000000000 ``` This commit fixes that by adding a check to handleTxsAvailable. Fixes #3908 * update changelog * schedule timeoutCommit if StartTime is in the future or SkipTimeoutCommit=true && we DON'T have all the votes * fix TestReactorCreatesBlockWhenEmptyBlocksFalse by checking if we have LastCommit or not * address Ethan's comments --- CHANGELOG_PENDING.md | 2 ++ consensus/state.go | 26 +++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 1b3cbc067..d75e29ce7 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -22,3 +22,5 @@ program](https://hackerone.com/tendermint). - [rpc] \#2010 Add NewHTTPWithClient and NewJSONRPCClientWithHTTPClient (note these and NewHTTP, NewJSONRPCClient functions panic if remote is invalid) (@gracenoah) ### BUG FIXES: + +- [consensus] \#3908 Wait `timeout_commit` to pass even if `create_empty_blocks` is `false` diff --git a/consensus/state.go b/consensus/state.go index c2ad00c95..50b5981e6 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -537,7 +537,7 @@ func (cs *ConsensusState) updateToState(state sm.State) { // We add timeoutCommit to allow transactions // to be gathered for the first block. // And alternative solution that relies on clocks: - // cs.StartTime = state.LastBlockTime.Add(timeoutCommit) + // cs.StartTime = state.LastBlockTime.Add(timeoutCommit) cs.StartTime = cs.config.Commit(tmtime.Now()) } else { cs.StartTime = cs.config.Commit(cs.CommitTime) @@ -756,9 +756,25 @@ func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) { func (cs *ConsensusState) handleTxsAvailable() { cs.mtx.Lock() defer cs.mtx.Unlock() - // we only need to do this for round 0 - cs.enterNewRound(cs.Height, 0) - cs.enterPropose(cs.Height, 0) + + // We only need to do this for round 0. + if cs.Round != 0 { + return + } + + switch cs.Step { + case cstypes.RoundStepNewHeight: // timeoutCommit phase + if cs.needProofBlock(cs.Height) { + // enterPropose will be called by enterNewRound + return + } + + // +1ms to ensure RoundStepNewRound timeout always happens after RoundStepNewHeight + timeoutCommit := cs.StartTime.Sub(tmtime.Now()) + 1*time.Millisecond + cs.scheduleTimeout(timeoutCommit, cs.Height, 0, cstypes.RoundStepNewRound) + case cstypes.RoundStepNewRound: // after timeoutCommit + cs.enterPropose(cs.Height, 0) + } } //----------------------------------------------------------------------------- @@ -766,7 +782,7 @@ func (cs *ConsensusState) handleTxsAvailable() { // Used internally by handleTimeout and handleMsg to make state transitions // Enter: `timeoutNewHeight` by startTime (commitTime+timeoutCommit), -// or, if SkipTimeout==true, after receiving all precommits from (height,round-1) +// or, if SkipTimeoutCommit==true, after receiving all precommits from (height,round-1) // Enter: `timeoutPrecommits` after any +2/3 precommits from (height,round-1) // Enter: +2/3 precommits for nil at (height,round-1) // Enter: +2/3 prevotes any or +2/3 precommits for block or any from (height, round)