@@ -406,6 +406,94 @@ func (ls *LedgerState) consumeUtxo(
406406 )
407407}
408408
409+ // calculateStabilityWindow returns the stability window based on the current era.
410+ // For Byron era, returns k. For Shelley+ eras, returns 3k/f.
411+ // Returns the default threshold if genesis data is unavailable or invalid.
412+ func (ls * LedgerState ) calculateStabilityWindow () uint64 {
413+ byronGenesis := ls .config .CardanoNodeConfig .ByronGenesis ()
414+ shelleyGenesis := ls .config .CardanoNodeConfig .ShelleyGenesis ()
415+ if byronGenesis == nil || shelleyGenesis == nil {
416+ return blockfetchBatchSlotThresholdDefault
417+ }
418+
419+ var securityParam uint64
420+ if ls .currentEra .Id == 0 { // Byron era
421+ k := byronGenesis .ProtocolConsts .K
422+ if k < 0 {
423+ ls .config .Logger .Warn ("invalid negative security parameter" , "k" , k )
424+ return blockfetchBatchSlotThresholdDefault
425+ }
426+ if k == 0 {
427+ ls .config .Logger .Warn ("security parameter is zero" , "k" , k )
428+ return blockfetchBatchSlotThresholdDefault
429+ }
430+ securityParam = uint64 (k ) // #nosec G115
431+ return securityParam // Byron uses k directly
432+ }
433+
434+ // Shelley+ eras
435+ k := shelleyGenesis .SecurityParam
436+ if k < 0 {
437+ ls .config .Logger .Warn ("invalid negative security parameter" , "k" , k )
438+ return blockfetchBatchSlotThresholdDefault
439+ }
440+ if k == 0 {
441+ ls .config .Logger .Warn ("security parameter is zero" , "k" , k )
442+ return blockfetchBatchSlotThresholdDefault
443+ }
444+ securityParam = uint64 (k )
445+
446+ // Calculate 3k/f
447+ activeSlotsCoeff := shelleyGenesis .ActiveSlotsCoeff .Rat
448+ if activeSlotsCoeff == nil {
449+ ls .config .Logger .Warn ("ActiveSlotsCoeff.Rat is nil" )
450+ return blockfetchBatchSlotThresholdDefault
451+ }
452+
453+ if activeSlotsCoeff .Num ().Sign () <= 0 {
454+ ls .config .Logger .Warn (
455+ "ActiveSlotsCoeff must be positive" ,
456+ "active_slots_coeff" ,
457+ activeSlotsCoeff .String (),
458+ )
459+ return blockfetchBatchSlotThresholdDefault
460+ }
461+
462+ numerator := new (big.Int ).SetUint64 (securityParam )
463+ numerator .Mul (numerator , big .NewInt (3 ))
464+ numerator .Mul (numerator , activeSlotsCoeff .Denom ())
465+ denominator := new (big.Int ).Set (activeSlotsCoeff .Num ())
466+ window , remainder := new (
467+ big.Int ,
468+ ).QuoRem (numerator , denominator , new (big.Int ))
469+ if remainder .Sign () != 0 {
470+ window .Add (window , big .NewInt (1 ))
471+ }
472+ if window .Sign () <= 0 {
473+ ls .config .Logger .Warn (
474+ "stability window calculation produced non-positive result" ,
475+ "security_param" ,
476+ securityParam ,
477+ "active_slots_coeff" ,
478+ activeSlotsCoeff .String (),
479+ )
480+ return blockfetchBatchSlotThresholdDefault
481+ }
482+ if ! window .IsUint64 () {
483+ ls .config .Logger .Warn (
484+ "stability window calculation overflowed uint64" ,
485+ "security_param" ,
486+ securityParam ,
487+ "active_slots_coeff" ,
488+ activeSlotsCoeff .String (),
489+ "window_num" ,
490+ window .String (),
491+ )
492+ return blockfetchBatchSlotThresholdDefault
493+ }
494+ return window .Uint64 ()
495+ }
496+
409497type readChainResult struct {
410498 rollbackPoint ocommon.Point
411499 blocks []ledger.Block
@@ -594,22 +682,42 @@ func (ls *LedgerState) ledgerProcessBlocks() {
594682 // Enable validation using the k-slot window from ShelleyGenesis.
595683 if ! shouldValidate && i == 0 {
596684 var cutoffSlot uint64
597- // Get parameters from Shelley Genesis
598- shelleyGenesis := ls .config .CardanoNodeConfig .ShelleyGenesis ()
599- if shelleyGenesis == nil {
600- return errors .New (
601- "failed to get Shelley Genesis config" ,
602- )
603- }
604- // Get security parameter (k)
605- k := shelleyGenesis .SecurityParam
606- if k < 0 {
607- return fmt .Errorf (
608- "security param must be non-negative: %d" ,
609- k ,
610- )
685+ // Get security parameter based on era
686+ var securityParam uint64
687+ if ls .currentEra .Id == 0 { // Byron era
688+ byronGenesis := ls .config .CardanoNodeConfig .ByronGenesis ()
689+ if byronGenesis == nil {
690+ return errors .New (
691+ "failed to get Byron Genesis config" ,
692+ )
693+ }
694+ k := byronGenesis .ProtocolConsts .K
695+ if k < 0 {
696+ return fmt .Errorf (
697+ "k parameter must be non-negative: %d" ,
698+ k ,
699+ )
700+ }
701+ // Safe conversion: K is non-negative
702+ securityParam = uint64 (k ) // #nosec G115
703+ } else { // Shelley and later eras
704+ shelleyGenesis := ls .config .CardanoNodeConfig .ShelleyGenesis ()
705+ if shelleyGenesis == nil {
706+ return errors .New (
707+ "failed to get Shelley Genesis config" ,
708+ )
709+ }
710+ // Get security parameter (k)
711+ k := shelleyGenesis .SecurityParam
712+ if k < 0 {
713+ return fmt .Errorf (
714+ "security param must be non-negative: %d" ,
715+ k ,
716+ )
717+ }
718+ // Safe conversion: SecurityParam is non-negative
719+ securityParam = uint64 (k ) // #nosec G115
611720 }
612- securityParam := uint64 (k )
613721 currentTipSlot := ls .currentTip .Point .Slot
614722 blockSlot := next .SlotNumber ()
615723 if currentTipSlot >= securityParam {
0 commit comments