@@ -14,6 +14,19 @@ const (
1414 defaultNumRoundsToWaitBeforeSignalingChronologyStuck = 10
1515)
1616
17+ const expectedSubroundsTimingCount = 4
18+
19+ var defaultConsensusConfigByRound = config.ConsensusConfigByRound {
20+ EnableRound : 0 ,
21+ SubroundsTiming : []config.SubroundTiming {
22+ {StartTime : 0.0 , EndTime : 0.05 },
23+ {StartTime : 0.05 , EndTime : 0.25 },
24+ {StartTime : 0.25 , EndTime : 0.85 },
25+ {StartTime : 0.85 , EndTime : 0.95 },
26+ },
27+ ProcessingThresholdPercent : 85 ,
28+ }
29+
1730// ErrEmptyCommonConfigsByEpoch signals that an empty common configs by epoch has been provided
1831var ErrEmptyCommonConfigsByEpoch = errors .New ("empty common configs by epoch" )
1932
@@ -24,13 +37,15 @@ type commonConfigs struct {
2437 orderedEpochStartConfigByEpoch []config.EpochStartConfigByEpoch
2538 orderedEpochStartConfigByRound []config.EpochStartConfigByRound
2639 orderedConsensusConfigByEpoch []config.ConsensusConfigByEpoch
40+ orderedConsensusConfigByRound []config.ConsensusConfigByRound
2741}
2842
2943// NewCommonConfigsHandler creates a new process configs by epoch component
3044func NewCommonConfigsHandler (
3145 configsByEpoch []config.EpochStartConfigByEpoch ,
3246 configsByRound []config.EpochStartConfigByRound ,
3347 consensusConfigByEpoch []config.ConsensusConfigByEpoch ,
48+ consensusConfigByRound []config.ConsensusConfigByRound ,
3449) (* commonConfigs , error ) {
3550 err := checkCommonConfigsByEpoch (configsByEpoch )
3651 if err != nil {
@@ -46,10 +61,16 @@ func NewCommonConfigsHandler(
4661 return nil , err
4762 }
4863
64+ err = checkConsensusConfigsByRound (consensusConfigByRound )
65+ if err != nil {
66+ return nil , err
67+ }
68+
4969 cc := & commonConfigs {
5070 orderedEpochStartConfigByEpoch : make ([]config.EpochStartConfigByEpoch , len (configsByEpoch )),
5171 orderedEpochStartConfigByRound : make ([]config.EpochStartConfigByRound , len (configsByRound )),
5272 orderedConsensusConfigByEpoch : make ([]config.ConsensusConfigByEpoch , len (consensusConfigByEpoch )),
73+ orderedConsensusConfigByRound : make ([]config.ConsensusConfigByRound , len (consensusConfigByRound )),
5374 }
5475
5576 // sort the config values in ascending order
@@ -68,6 +89,11 @@ func NewCommonConfigsHandler(
6889 return cc .orderedConsensusConfigByEpoch [i ].EnableEpoch < cc .orderedConsensusConfigByEpoch [j ].EnableEpoch
6990 })
7091
92+ copy (cc .orderedConsensusConfigByRound , consensusConfigByRound )
93+ sort .SliceStable (cc .orderedConsensusConfigByRound , func (i , j int ) bool {
94+ return cc .orderedConsensusConfigByRound [i ].EnableRound < cc .orderedConsensusConfigByRound [j ].EnableRound
95+ })
96+
7197 return cc , nil
7298}
7399
@@ -140,6 +166,71 @@ func checkConsensusConfigsByEpoch(configsByEpoch []config.ConsensusConfigByEpoch
140166 return nil
141167}
142168
169+ func checkConsensusConfigsByRound (configsByRound []config.ConsensusConfigByRound ) error {
170+ if len (configsByRound ) == 0 {
171+ return ErrEmptyConsensusConfigsByRound
172+ }
173+
174+ // check for duplicated rounds
175+ seen := make (map [uint64 ]struct {})
176+ for _ , cfg := range configsByRound {
177+ _ , exists := seen [cfg .EnableRound ]
178+ if exists {
179+ return ErrDuplicatedRoundConfig
180+ }
181+ seen [cfg .EnableRound ] = struct {}{}
182+
183+ if err := checkSubroundsTiming (cfg ); err != nil {
184+ return err
185+ }
186+ }
187+
188+ _ , exists := seen [0 ]
189+ if ! exists {
190+ return ErrMissingRoundZeroConfig
191+ }
192+
193+ return nil
194+ }
195+
196+ func checkSubroundsTiming (cfg config.ConsensusConfigByRound ) error {
197+ // the slice must contain exactly one entry per subround
198+ if len (cfg .SubroundsTiming ) != expectedSubroundsTimingCount {
199+ return ErrInvalidSubroundsTimingCount
200+ }
201+
202+ // all values must be non-negative and each subround must have start < end
203+ for _ , t := range cfg .SubroundsTiming {
204+ if t .StartTime < 0 || t .EndTime < 0 {
205+ return ErrNegativeSubroundTiming
206+ }
207+ if t .StartTime >= t .EndTime {
208+ return ErrInvalidSubroundTimingRange
209+ }
210+ }
211+
212+ // subrounds must be ordered, contiguous (no gaps), and non-overlapping
213+ for i := 1 ; i < len (cfg .SubroundsTiming ); i ++ {
214+ if cfg .SubroundsTiming [i ].StartTime != cfg .SubroundsTiming [i - 1 ].EndTime {
215+ return ErrOverlappingSubroundTiming
216+ }
217+ }
218+
219+ // all boundary values must be < 1.0
220+ for _ , t := range cfg .SubroundsTiming {
221+ if t .StartTime >= 1.0 || t .EndTime >= 1.0 {
222+ return ErrSubroundTimingExceedsRound
223+ }
224+ }
225+
226+ // ProcessingThresholdPercent must be in (0, 100]
227+ if cfg .ProcessingThresholdPercent == 0 || cfg .ProcessingThresholdPercent > 100 {
228+ return ErrInvalidProcessingThreshold
229+ }
230+
231+ return nil
232+ }
233+
143234// GetGracePeriodRoundsByEpoch returns the grace period rounds by epoch
144235func (cc * commonConfigs ) GetGracePeriodRoundsByEpoch (epoch uint32 ) uint32 {
145236 for i := len (cc .orderedEpochStartConfigByEpoch ) - 1 ; i >= 0 ; i -- {
@@ -184,6 +275,28 @@ func (cc *commonConfigs) GetNumRoundsToWaitBeforeSignalingChronologyStuck(epoch
184275 return defaultNumRoundsToWaitBeforeSignalingChronologyStuck // this should not happen
185276}
186277
278+ // GetSubroundsTimingByRound returns the subrounds timing configuration active for the given round
279+ func (cc * commonConfigs ) GetSubroundsTimingByRound (round uint64 ) config.ConsensusConfigByRound {
280+ for i := len (cc .orderedConsensusConfigByRound ) - 1 ; i >= 0 ; i -- {
281+ if cc .orderedConsensusConfigByRound [i ].EnableRound <= round {
282+ return cc .orderedConsensusConfigByRound [i ]
283+ }
284+ }
285+
286+ return defaultConsensusConfigByRound // this should not happen
287+ }
288+
289+ // GetActiveTimingBoundaryRound returns the EnableRound of the ConsensusConfigByRound entry that is active for the given round
290+ func (cc * commonConfigs ) GetActiveTimingBoundaryRound (round uint64 ) uint64 {
291+ for i := len (cc .orderedConsensusConfigByRound ) - 1 ; i >= 0 ; i -- {
292+ if cc .orderedConsensusConfigByRound [i ].EnableRound <= round {
293+ return cc .orderedConsensusConfigByRound [i ].EnableRound
294+ }
295+ }
296+
297+ return 0
298+ }
299+
187300// IsInterfaceNil checks if the instance is nil
188301func (cc * commonConfigs ) IsInterfaceNil () bool {
189302 return cc == nil
0 commit comments