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"`
// Block time parameters in milliseconds
BlockTimeIota int `mapstructure:"blocktime_iota"`
BlockTimeWiggle int `mapstructure:"blocktime_wiggle"`
BlockTimeWiggleR float64 `mapstructure:"blocktime_wiggle_r"`
BlockTimeIota int `mapstructure:"blocktime_iota"`
BlockTimeWiggle int `mapstructure:"blocktime_wiggle"`
BlockTimeWiggleDelta int `mapstructure:"blocktime_wiggle_delta"`
}
// DefaultConsensusConfig returns a default configuration for the consensus service
@ -421,7 +421,7 @@ func DefaultConsensusConfig() *ConsensusConfig {
PeerQueryMaj23SleepDuration: 2000,
BlockTimeIota: 10,
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
// of "subjective time validity". See the BFT time spec.
func (cfg *ConsensusConfig) BlockTimeMinValidTime(lastBlockTime, now time.Time, round int) time.Time {
var minValidTime time.Time = lastBlockTime.Add(time.Duration(cfg.BlockTimeIota) * time.Millisecond)
if round == 0 {
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
func (cfg *ConsensusConfig) BlockTimeMinValidTime(lastBlockTime time.Time) time.Time {
return lastBlockTime.
Add(time.Duration(cfg.BlockTimeIota) * time.Millisecond)
}
// BlockTimeMaxValidTime returns the maximum acceptable block time, as part
// 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.
Add(time.Duration(cfg.BlockTimeWiggle) * time.Millisecond).
Add(
time.Duration(
float64(cfg.BlockTimeWiggle)*cfg.BlockTimeWiggleR*float64(round),
int64(cfg.BlockTimeWiggleDelta)*int64(round),
) * time.Millisecond,
)
}

View File

@ -945,7 +945,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
// See the BFT time spec.
lastBlockTime := cs.state.LastBlockTime
now := time.Now().Round(0).UTC()
minValidTime := cs.config.BlockTimeMinValidTime(lastBlockTime, now, round)
minValidTime := cs.config.BlockTimeMinValidTime(lastBlockTime)
if cs.ProposalBlock.Time.Before(minValidTime) {
logger.Info("enterPrevote: ProposalBlock time too low",
"blockTime", cs.ProposalBlock.Time,
@ -953,7 +953,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{})
return
}
maxValidTime := cs.config.BlockTimeMaxValidTime(lastBlockTime, now, round)
maxValidTime := cs.config.BlockTimeMaxValidTime(now, round)
if maxValidTime.Before(cs.ProposalBlock.Time) {
logger.Info("enterPrevote: ProposalBlock time too high",
"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
property, but only when signing a prevote for a block:
- **Subjective Time Validity**: Time is greater than MinValidTime(last_block_time,
now, round) and less than or equal to MaxValidTime(last_block_time, now), where:
- **Subjective Time Validity**: Time is greater than MinValidTime(last_block_time) and less than or equal to MaxValidTime(now, round), where:
```go
// wiggle and iota are provided by consensus config.
func MinValidTime(last_block_time, now time.Time, round int) time.Time {
var minValidTime time.Time = last_block_time.Add(iota)
if round == 0 {
minValidTime = maxTime(minValidTime, now.Add(-1*wiggle)
} else {
// For all subsequent rounds, we accept any block > last_block_time+iota.
}
return minValidTime
// iota provided by consensus config.
func MinValidTime(last_block_time) time.Time {
return lastBlockTime.
Add(time.Duration(iota) * time.Millisecond)
}
// wiggle and wiggle_r are provided by consensus config.
func MaxValidTime(last_block_time, now time.Time, round int) time.Time {
// wiggle and wiggle_delta are provided by consensus config.
func MaxValidTime(now time.Time, round int) time.Time {
return now.
Add(wiggle).
Add(wiggle*wiggle_r*round)
Add(time.Duration(wiggle) * time.Millisecond).
Add(
time.Duration(
wiggle_delta*float64(round),
) * time.Millisecond,
)
}
```
For `MinValidTime`, we only accept recent blocks (`wiggle`) on the first
round. This has the effect of slightly slowing down the blockchain (requiring
at least two rounds of consensus instead of one) progressively as more validator
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 `MinValidTime`, we accept any block that is at least `iota` greater than
the last block time. Blocks that are significantly older than the current time
can still be valid, which allows for the re-proposing of older proposals.
For `MaxValidTime`, we accept blocks where the block time is greater than `now`
plus some threshold that increases linearly with the round number.
The purpose of `wiggle_r` is for graceful degredation when +2/3 validators
*aren't* within `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!
For `MaxValidTime`, we accept block times greater than `now` plus some
threshold that increases linearly with the round number. The purpose of
`wiggle_delta` is for graceful degredation when +2/3 validators *aren't* within
`wiggle` of each other but are otherwise non-Byzantine in all other respects.
NOTE: `wiggle_r` could be set to something like 0.05 (e.g. if `wiggle` were 20s, `wiggle*wiggle_r` would be 1s.)
`wiggle*wiggle_r` 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_r` 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.
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. Without
`wiggle_delta`, 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_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
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.