Removed MinValidTime Round0 exception; WiggleR -> WiggleDelta

This commit is contained in:
Jae Kwon 2018-05-23 17:10:04 -07:00
parent b4120e25ff
commit 4ec86eea39
3 changed files with 45 additions and 59 deletions

View File

@ -396,9 +396,9 @@ type ConsensusConfig struct {
PeerQueryMaj23SleepDuration int `mapstructure:"peer_query_maj23_sleep_duration"` PeerQueryMaj23SleepDuration int `mapstructure:"peer_query_maj23_sleep_duration"`
// Block time parameters in milliseconds // Block time parameters in milliseconds
BlockTimeIota int `mapstructure:"blocktime_iota"` BlockTimeIota int `mapstructure:"blocktime_iota"`
BlockTimeWiggle int `mapstructure:"blocktime_wiggle"` BlockTimeWiggle int `mapstructure:"blocktime_wiggle"`
BlockTimeWiggleR float64 `mapstructure:"blocktime_wiggle_r"` BlockTimeWiggleDelta int `mapstructure:"blocktime_wiggle_delta"`
} }
// DefaultConsensusConfig returns a default configuration for the consensus service // DefaultConsensusConfig returns a default configuration for the consensus service
@ -421,7 +421,7 @@ func DefaultConsensusConfig() *ConsensusConfig {
PeerQueryMaj23SleepDuration: 2000, PeerQueryMaj23SleepDuration: 2000,
BlockTimeIota: 10, BlockTimeIota: 10,
BlockTimeWiggle: 20000, BlockTimeWiggle: 20000,
BlockTimeWiggleR: 0.05, BlockTimeWiggleDelta: 1000,
} }
} }
@ -487,27 +487,19 @@ func (cfg *ConsensusConfig) PeerQueryMaj23Sleep() time.Duration {
// BlockTimeMinValidTime returns the minimum acceptable block time, as part // BlockTimeMinValidTime returns the minimum acceptable block time, as part
// of "subjective time validity". See the BFT time spec. // of "subjective time validity". See the BFT time spec.
func (cfg *ConsensusConfig) BlockTimeMinValidTime(lastBlockTime, now time.Time, round int) time.Time { func (cfg *ConsensusConfig) BlockTimeMinValidTime(lastBlockTime time.Time) time.Time {
var minValidTime time.Time = lastBlockTime.Add(time.Duration(cfg.BlockTimeIota) * time.Millisecond) return lastBlockTime.
if round == 0 { Add(time.Duration(cfg.BlockTimeIota) * time.Millisecond)
wiggleAgo := now.Add(-1 * time.Duration(cfg.BlockTimeWiggle) * time.Millisecond)
if wiggleAgo.After(minValidTime) {
minValidTime = wiggleAgo
}
} else {
// For all subsequent rounds, we accept any block > last_block_time+iota.
}
return minValidTime
} }
// BlockTimeMaxValidTime returns the maximum acceptable block time, as part // BlockTimeMaxValidTime returns the maximum acceptable block time, as part
// of "subjective time validity". See the BFT time spec. // of "subjective time validity". See the BFT time spec.
func (cfg *ConsensusConfig) BlockTimeMaxValidTime(lastBlockTime, now time.Time, round int) time.Time { func (cfg *ConsensusConfig) BlockTimeMaxValidTime(now time.Time, round int) time.Time {
return now. return now.
Add(time.Duration(cfg.BlockTimeWiggle) * time.Millisecond). Add(time.Duration(cfg.BlockTimeWiggle) * time.Millisecond).
Add( Add(
time.Duration( time.Duration(
float64(cfg.BlockTimeWiggle)*cfg.BlockTimeWiggleR*float64(round), int64(cfg.BlockTimeWiggleDelta)*int64(round),
) * time.Millisecond, ) * time.Millisecond,
) )
} }

View File

@ -945,7 +945,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
// See the BFT time spec. // See the BFT time spec.
lastBlockTime := cs.state.LastBlockTime lastBlockTime := cs.state.LastBlockTime
now := time.Now().Round(0).UTC() now := time.Now().Round(0).UTC()
minValidTime := cs.config.BlockTimeMinValidTime(lastBlockTime, now, round) minValidTime := cs.config.BlockTimeMinValidTime(lastBlockTime)
if cs.ProposalBlock.Time.Before(minValidTime) { if cs.ProposalBlock.Time.Before(minValidTime) {
logger.Info("enterPrevote: ProposalBlock time too low", logger.Info("enterPrevote: ProposalBlock time too low",
"blockTime", cs.ProposalBlock.Time, "blockTime", cs.ProposalBlock.Time,
@ -953,7 +953,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{}) cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{})
return return
} }
maxValidTime := cs.config.BlockTimeMaxValidTime(lastBlockTime, now, round) maxValidTime := cs.config.BlockTimeMaxValidTime(now, round)
if maxValidTime.Before(cs.ProposalBlock.Time) { if maxValidTime.Before(cs.ProposalBlock.Time) {
logger.Info("enterPrevote: ProposalBlock time too high", logger.Info("enterPrevote: ProposalBlock time too high",
"blockTime", cs.ProposalBlock.Time, "blockTime", cs.ProposalBlock.Time,

View File

@ -13,55 +13,49 @@ a header H1 for height h1 and a header H2 for height `h2 = h1 + 1`, `H1.Time < H
Beyond satisfying time monotinicity, Tendermint also checks the following Beyond satisfying time monotinicity, Tendermint also checks the following
property, but only when signing a prevote for a block: property, but only when signing a prevote for a block:
- **Subjective Time Validity**: Time is greater than MinValidTime(last_block_time, - **Subjective Time Validity**: Time is greater than MinValidTime(last_block_time) and less than or equal to MaxValidTime(now, round), where:
now, round) and less than or equal to MaxValidTime(last_block_time, now), where:
```go ```go
// wiggle and iota are provided by consensus config. // iota provided by consensus config.
func MinValidTime(last_block_time, now time.Time, round int) time.Time { func MinValidTime(last_block_time) time.Time {
var minValidTime time.Time = last_block_time.Add(iota) return lastBlockTime.
if round == 0 { Add(time.Duration(iota) * time.Millisecond)
minValidTime = maxTime(minValidTime, now.Add(-1*wiggle)
} else {
// For all subsequent rounds, we accept any block > last_block_time+iota.
}
return minValidTime
} }
// wiggle and wiggle_r are provided by consensus config. // wiggle and wiggle_delta are provided by consensus config.
func MaxValidTime(last_block_time, now time.Time, round int) time.Time { func MaxValidTime(now time.Time, round int) time.Time {
return now. return now.
Add(wiggle). Add(time.Duration(wiggle) * time.Millisecond).
Add(wiggle*wiggle_r*round) Add(
time.Duration(
wiggle_delta*float64(round),
) * time.Millisecond,
)
} }
``` ```
For `MinValidTime`, we only accept recent blocks (`wiggle`) on the first For `MinValidTime`, we accept any block that is at least `iota` greater than
round. This has the effect of slightly slowing down the blockchain (requiring the last block time. Blocks that are significantly older than the current time
at least two rounds of consensus instead of one) progressively as more validator can still be valid, which allows for the re-proposing of older proposals.
clocks get off sync from each other. Blocks that are significantly older than
`now` can still be valid (except in round 0), which allows for the re-proposing of older proposals.
The blockchain's time "eventually" catches up over block heights to a reasonably recent time as long as
time-correct validators' proposals are committed in a timely fashion (i.e. less than 1/3
are Byzantine) TODO: Quantify "eventually" as a function of % of time-correct
validators.
For `MaxValidTime`, we accept blocks where the block time is greater than `now` For `MaxValidTime`, we accept block times greater than `now` plus some
plus some threshold that increases linearly with the round number. threshold that increases linearly with the round number. The purpose of
The purpose of `wiggle_r` is for graceful degredation when +2/3 validators `wiggle_delta` is for graceful degredation when +2/3 validators *aren't* within
*aren't* within `wiggle` of each other but are otherwise non-Byzantine in all other respects. `wiggle` of each other but are otherwise non-Byzantine in all other respects.
Consider an example with 100 equally weighted validators, where 33 are Byzantine,
and one of the remaining 67 validators has a faulty clock that causes it to drift
back more than `wiggle` from the other 66. If the 33 Byzantine
validators were to withhold their votes, no block would produce a Polka until the
drifting one becomes the proposer!
NOTE: `wiggle_r` could be set to something like 0.05 (e.g. if `wiggle` were 20s, `wiggle*wiggle_r` would be 1s.) Consider an example with 100 equally weighted validators, where 33 are
`wiggle*wiggle_r` should probably be less than `timeout_propose_delta` to prevent unnecessary Byzantine, and one of the remaining 67 validators has a faulty clock that
forward time-jumps in cases where higher rounds are reached due to factors causes it to drift back more than `wiggle` from the other 66. Without
other than network performance -- for example, where the network is performant `wiggle_delta`, if the 33 Byzantine validators were to withhold their votes, no
but several proposers were absent in a row. `wiggle_r` could theoretically be set to 0 block would produce a Polka until the drifting one becomes the proposer.
if it can be assumed that +2/3 (by voting power) of correct validators' clocks are within `wiggle` of each other.
NOTE: `wiggle_delta` should probably be less than `timeout_propose_delta` to
prevent unnecessary forward time-jumps in cases where higher rounds are reached
due to factors other than network performance -- for example, where the network
is performant but several proposers were absent in a row. `wiggle_delta` could
theoretically be set to 0 if it can be assumed that +2/3 (by voting power) of
correct validators' clocks are within `wiggle` of each other.
Subjective time validity is ignored when a Polka or Commit is found, allowing Subjective time validity is ignored when a Polka or Commit is found, allowing
consensus to progress locally even when the subjective time requirements are not satisfied. consensus to progress locally even when the subjective time requirements are
not satisfied.