@@ -36,9 +36,8 @@ const (
3636 // This prevents us exceeding the configured recv queue size in the block-fetch protocol
3737 blockfetchBatchSize = 500
3838
39- // TODO: calculate from protocol params
40- // Number of slots from upstream tip to stop doing blockfetch batches
41- blockfetchBatchSlotThreshold = 2500 * 20
39+ // Default/fallback slot threshold for blockfetch batches
40+ blockfetchBatchSlotThresholdDefault = 2500 * 20
4241
4342 // Timeout for updates on a blockfetch operation. This is based on a 2s BatchStart
4443 // and a 2s Block timeout for blockfetch
@@ -141,8 +140,80 @@ func (ls *LedgerState) handleEventChainsyncBlockHeader(e ChainsyncEvent) error {
141140 }
142141 // Wait for additional block headers before fetching block bodies if we're
143142 // far enough out from upstream tip
143+ // Use security window as slot threshold if available
144+ slotThreshold := uint64 (blockfetchBatchSlotThresholdDefault )
145+ byronGenesis := ls .config .CardanoNodeConfig .ByronGenesis ()
146+ shelleyGenesis := ls .config .CardanoNodeConfig .ShelleyGenesis ()
147+ if byronGenesis != nil && shelleyGenesis != nil {
148+ var securityParam uint64
149+ var validParams bool
150+
151+ // Use K parameter from Byron Genesis during Byron era, SecurityParam from Shelley Genesis for later eras
152+ if ls .currentEra .Id == 0 { // Byron era ID is 0
153+ k := byronGenesis .ProtocolConsts .K
154+ if k < 0 {
155+ ls .config .Logger .Warn ("Byron K parameter is negative, using fallback" , "k" , k )
156+ validParams = false
157+ } else {
158+ // Safe conversion: K is non-negative
159+ securityParam = uint64 (k ) // #nosec G115
160+ validParams = true
161+ }
162+ } else {
163+ secParam := shelleyGenesis .SecurityParam
164+ if secParam < 0 {
165+ ls .config .Logger .Warn ("Shelley SecurityParam is negative, using fallback" , "securityParam" , secParam )
166+ validParams = false
167+ } else {
168+ // Safe conversion: SecurityParam is non-negative
169+ securityParam = uint64 (secParam ) // #nosec G115
170+ validParams = true
171+ }
172+ }
173+
174+ if validParams {
175+ // Calculate stability window: 3k/f for Shelley+ eras, just k for Byron
176+ var stabilityWindow uint64
177+ if ls .currentEra .Id == 0 { // Byron era
178+ stabilityWindow = securityParam
179+ } else {
180+ // Validate ActiveSlotsCoeff before using it
181+ if shelleyGenesis .ActiveSlotsCoeff .Rat == nil {
182+ ls .config .Logger .Warn ("Shelley ActiveSlotsCoeff.Rat is nil, using fallback" )
183+ validParams = false
184+ } else {
185+ activeSlotsRat := shelleyGenesis .ActiveSlotsCoeff .Rat
186+ // Check for division by zero
187+ if activeSlotsRat .Num ().Sign () == 0 {
188+ ls .config .Logger .Warn ("Shelley ActiveSlotsCoeff numerator is zero, using fallback" )
189+ validParams = false
190+ } else {
191+ // Compute stability window: floor((3 * securityParam) / f)
192+ // Using integer arithmetic to avoid floating point precision issues
193+ numerator := new (big.Int ).SetUint64 (3 * securityParam )
194+ quotient := new (big.Int ).Div (
195+ new (big.Int ).Mul (numerator , activeSlotsRat .Denom ()),
196+ activeSlotsRat .Num (),
197+ )
198+
199+ // Check if result fits in uint64
200+ if ! quotient .IsUint64 () {
201+ ls .config .Logger .Warn ("Stability window calculation result too large, using fallback" )
202+ validParams = false
203+ } else {
204+ stabilityWindow = quotient .Uint64 ()
205+ }
206+ }
207+ }
208+ }
209+
210+ if validParams {
211+ slotThreshold = stabilityWindow
212+ }
213+ }
214+ }
144215 if e .Point .Slot < e .Tip .Point .Slot &&
145- (e .Tip .Point .Slot - e .Point .Slot > blockfetchBatchSlotThreshold ) &&
216+ (e .Tip .Point .Slot - e .Point .Slot > slotThreshold ) &&
146217 (headerCount + 1 ) < allowedHeaderCount {
147218 return nil
148219 }
@@ -272,13 +343,51 @@ func (ls *LedgerState) calculateEpochNonce(
272343 if byronGenesis == nil || shelleyGenesis == nil {
273344 return nil , errors .New ("could not get genesis config" )
274345 }
275- stabilityWindow := new (big.Rat ).Quo (
276- big .NewRat (
277- int64 (3 * byronGenesis .ProtocolConsts .K ),
278- 1 ,
279- ),
280- shelleyGenesis .ActiveSlotsCoeff .Rat ,
281- ).Num ().Uint64 ()
346+ var securityParam uint64
347+ // Use K parameter from Byron Genesis during Byron era, SecurityParam from Shelley Genesis for later eras
348+ if ls .currentEra .Id == 0 { // Byron era ID is 0
349+ k := byronGenesis .ProtocolConsts .K
350+ if k < 0 {
351+ return nil , fmt .Errorf ("k parameter must be non-negative: %d" , k )
352+ }
353+ // Safe conversion: K is non-negative
354+ securityParam = uint64 (k ) // #nosec G115
355+ } else {
356+ secParam := shelleyGenesis .SecurityParam
357+ if secParam < 0 {
358+ return nil , fmt .Errorf ("security param must be non-negative: %d" , secParam )
359+ }
360+ // Safe conversion: SecurityParam is non-negative
361+ securityParam = uint64 (secParam ) // #nosec G115
362+ }
363+ // Calculate stability window: 3k/f for Shelley+ eras, just k for Byron
364+ var stabilityWindow uint64
365+ if ls .currentEra .Id == 0 { // Byron era
366+ stabilityWindow = securityParam
367+ } else {
368+ // Validate ActiveSlotsCoeff before using it
369+ if shelleyGenesis .ActiveSlotsCoeff .Rat == nil {
370+ return nil , errors .New ("empty ActiveSlotsCoeff" )
371+ }
372+ activeSlotsRat := shelleyGenesis .ActiveSlotsCoeff .Rat
373+ // Check for division by zero
374+ if activeSlotsRat .Num ().Sign () == 0 {
375+ return nil , errors .New ("divide by zero" )
376+ }
377+ // Compute stability window: floor((3 * securityParam) / f)
378+ // Using integer arithmetic to avoid floating point precision issues
379+ numerator := new (big.Int ).SetUint64 (3 * securityParam )
380+ quotient := new (big.Int ).Div (
381+ new (big.Int ).Mul (numerator , activeSlotsRat .Denom ()),
382+ activeSlotsRat .Num (),
383+ )
384+
385+ // Check if result fits in uint64
386+ if ! quotient .IsUint64 () {
387+ return nil , errors .New ("stability window calculation result too large" )
388+ }
389+ stabilityWindow = quotient .Uint64 ()
390+ }
282391 var stabilityWindowStartSlot uint64
283392 if epochStartSlot > stabilityWindow {
284393 stabilityWindowStartSlot = epochStartSlot - stabilityWindow
0 commit comments