github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/pacemaker/timeout/config.go (about) 1 package timeout 2 3 import ( 4 "fmt" 5 "math" 6 "time" 7 8 "github.com/rs/zerolog/log" 9 "go.uber.org/atomic" 10 11 "github.com/koko1123/flow-go-1/consensus/hotstuff/model" 12 "github.com/koko1123/flow-go-1/module/updatable_configs" 13 ) 14 15 // Config contains the configuration parameters for ExponentialIncrease-LinearDecrease 16 // timeout.Controller 17 // - on timeout: increase timeout by multiplicative factor `timeoutIncrease` (user-specified) 18 // this results in exponential growing timeout duration on multiple subsequent timeouts 19 // - on progress: MULTIPLICATIVE timeout decrease 20 type Config struct { 21 // ReplicaTimeout is the duration of a view before we time out [MILLISECONDS] 22 // ReplicaTimeout is the only variable quantity 23 ReplicaTimeout float64 24 // MinReplicaTimeout is the minimum the timeout can decrease to [MILLISECONDS] 25 MinReplicaTimeout float64 26 // VoteAggregationTimeoutFraction is the FRACTION of ReplicaTimeout which the Primary 27 // will maximally wait to collect enough votes before building a block (with an old qc) 28 VoteAggregationTimeoutFraction float64 29 // TimeoutDecrease: MULTIPLICATIVE factor for increasing timeout on timeout 30 TimeoutIncrease float64 31 // TimeoutDecrease: MULTIPLICATIVE factor for decreasing timeout on progress 32 TimeoutDecrease float64 33 // BlockRateDelayMS is a delay to broadcast the proposal in order to control block production rate [MILLISECONDS] 34 BlockRateDelayMS *atomic.Float64 35 } 36 37 var DefaultConfig = NewDefaultConfig() 38 39 // NewDefaultConfig returns a default timeout configuration. 40 // We explicitly provide a method here, which demonstrates in-code how 41 // to compute standard values from some basic quantities. 42 func NewDefaultConfig() Config { 43 // the replicas will start with 60 second time out to allow all other replicas to come online 44 // once the replica's views are synchronized, the timeout will decrease to more reasonable values 45 replicaTimeout := 60 * time.Second 46 47 // the lower bound on the replicaTimeout value 48 // If HotStuff is running at full speed, 1200ms should be enough. However, we add some buffer. 49 // This value is for instant message delivery. 50 minReplicaTimeout := 2 * time.Second 51 timeoutIncreaseFactor := 2.0 52 blockRateDelay := 0 * time.Millisecond 53 54 // the following demonstrates the computation of standard values 55 conf, err := NewConfig( 56 replicaTimeout, 57 minReplicaTimeout+blockRateDelay, 58 StandardVoteAggregationTimeoutFraction(minReplicaTimeout, blockRateDelay), // resulting value here is 0.5 59 timeoutIncreaseFactor, 60 StandardTimeoutDecreaseFactor(1.0/3.0, timeoutIncreaseFactor), // resulting value is 1/sqrt(2) 61 blockRateDelay, 62 ) 63 if err != nil { 64 // we check in a unit test that this does not happen 65 panic("Default config is not compliant with timeout Config requirements") 66 } 67 68 return conf 69 } 70 71 // NewConfig creates a new TimoutConfig. 72 // startReplicaTimeout: starting timeout value for replica round [Milliseconds]; 73 // minReplicaTimeout: minimal timeout value for replica round [Milliseconds]; 74 // voteAggregationTimeoutFraction: fraction of replicaTimeout which is reserved for aggregating votes; 75 // timeoutIncrease: multiplicative factor for increasing timeout; 76 // timeoutDecrease: linear subtrahend for timeout decrease [Milliseconds] 77 // blockRateDelay: a delay to delay the proposal broadcasting 78 func NewConfig( 79 startReplicaTimeout time.Duration, 80 minReplicaTimeout time.Duration, 81 voteAggregationTimeoutFraction float64, 82 timeoutIncrease float64, 83 timeoutDecrease float64, 84 blockRateDelay time.Duration, 85 ) (Config, error) { 86 if startReplicaTimeout < minReplicaTimeout { 87 return Config{}, model.NewConfigurationErrorf("startReplicaTimeout (%dms) cannot be smaller than minReplicaTimeout (%dms)", 88 startReplicaTimeout.Milliseconds(), minReplicaTimeout.Milliseconds()) 89 } 90 if minReplicaTimeout < 0 { 91 return Config{}, model.NewConfigurationErrorf("minReplicaTimeout must non-negative") 92 } 93 if voteAggregationTimeoutFraction <= 0 || 1 < voteAggregationTimeoutFraction { 94 return Config{}, model.NewConfigurationErrorf("VoteAggregationTimeoutFraction must be in range (0,1]") 95 } 96 if timeoutIncrease <= 1 { 97 return Config{}, model.NewConfigurationErrorf("TimeoutIncrease must be strictly bigger than 1") 98 } 99 if timeoutDecrease <= 0 || 1 <= timeoutDecrease { 100 return Config{}, model.NewConfigurationErrorf("timeoutDecrease must be in range (0,1)") 101 } 102 if err := validBlockRateDelay(blockRateDelay); err != nil { 103 return Config{}, err 104 } 105 106 tc := Config{ 107 ReplicaTimeout: float64(startReplicaTimeout.Milliseconds()), 108 MinReplicaTimeout: float64(minReplicaTimeout.Milliseconds()), 109 VoteAggregationTimeoutFraction: voteAggregationTimeoutFraction, 110 TimeoutIncrease: timeoutIncrease, 111 TimeoutDecrease: timeoutDecrease, 112 BlockRateDelayMS: atomic.NewFloat64(float64(blockRateDelay.Milliseconds())), 113 } 114 return tc, nil 115 } 116 117 // validBlockRateDelay validates a block rate delay config. 118 // Returns model.ConfigurationError for invalid config inputs. 119 func validBlockRateDelay(blockRateDelay time.Duration) error { 120 if blockRateDelay < 0 { 121 return model.NewConfigurationErrorf("blockRateDelay must be must be non-negative") 122 } 123 return nil 124 } 125 126 // GetBlockRateDelay returns the block rate delay as a Duration. This is used by 127 // the dyamic config manager. 128 func (c *Config) GetBlockRateDelay() time.Duration { 129 ms := c.BlockRateDelayMS.Load() 130 return time.Millisecond * time.Duration(ms) 131 } 132 133 // SetBlockRateDelay sets the block rate delay. It is used to modify this config 134 // value while HotStuff is running. 135 // Returns updatable_configs.ValidationError if the new value is invalid. 136 func (c *Config) SetBlockRateDelay(delay time.Duration) error { 137 if err := validBlockRateDelay(delay); err != nil { 138 if model.IsConfigurationError(err) { 139 return updatable_configs.NewValidationErrorf("invalid block rate delay: %w", err) 140 } 141 return fmt.Errorf("unexpected error validating block rate delay: %w", err) 142 } 143 // sanity check: log a warning if we set block rate delay above min timeout 144 // it is valid to want to do this, to significantly slow the block rate, but 145 // only in edge cases 146 if c.MinReplicaTimeout < float64(delay.Milliseconds()) { 147 log.Warn().Msgf("CAUTION: setting block rate delay to %s, above min timeout %dms - this will degrade performance!", delay.String(), int64(c.MinReplicaTimeout)) 148 } 149 c.BlockRateDelayMS.Store(float64(delay.Milliseconds())) 150 return nil 151 } 152 153 // StandardVoteAggregationTimeoutFraction calculates a standard value for the VoteAggregationTimeoutFraction in case a block delay is used. 154 // The motivation for the standard value is as follows: 155 // - the next primary receives the block it ideally would extend at some time t 156 // - the best guess the primary has, when other nodes would receive the block is at time t as well 157 // - the primary needs to get its block to the other replicas, before they time out: 158 // the primary uses its own timeout as estimator for the other replicas' timeout 159 func StandardVoteAggregationTimeoutFraction(minReplicaTimeout time.Duration, blockRateDelay time.Duration) float64 { 160 standardVoteAggregationTimeoutFraction := 0.5 161 minReplicaTimeoutMS := float64(minReplicaTimeout.Milliseconds()) 162 blockRateDelayMS := float64(blockRateDelay.Milliseconds()) 163 return (standardVoteAggregationTimeoutFraction*minReplicaTimeoutMS + blockRateDelayMS) / (minReplicaTimeoutMS + blockRateDelayMS) 164 } 165 166 // StandardTimeoutDecreaseFactor calculates a standard value for TimeoutDecreaseFactor 167 // for an assumed max fraction of offline (byzantine) HotStuff committee members 168 func StandardTimeoutDecreaseFactor(maxFractionOfflineReplicas, timeoutIncreaseFactor float64) float64 { 169 return math.Pow(timeoutIncreaseFactor, maxFractionOfflineReplicas/(maxFractionOfflineReplicas-1)) 170 }