Skip to content

Commit 4a59bd9

Browse files
fix: use stability window for slot threshold
- Replace hard-coded blockfetchBatchSlotThreshold with dynamic calculation - Use correct security parameters based on current era: * Byron era: K parameter from ByronGenesis.ProtocolConsts.K * Shelley+ eras: SecurityParam from ShelleyGenesis.SecurityParam - Update stability window calculation in both chainsync and validation logic - Calculate stability window as 3k/f for Shelley+ eras, k for Byron era - Ensures blockfetch operations respect protocol-defined stability window Fixes the TODO to calculate slot threshold from protocol params Signed-off-by: Chris Gianelloni <wolf31o2@blinklabs.io> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent b6654c8 commit 4a59bd9

File tree

4 files changed

+1639
-29
lines changed

4 files changed

+1639
-29
lines changed

ledger/chainsync.go

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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,10 @@ 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 := ls.calculateStabilityWindow()
144145
if e.Point.Slot < e.Tip.Point.Slot &&
145-
(e.Tip.Point.Slot-e.Point.Slot > blockfetchBatchSlotThreshold) &&
146+
(e.Tip.Point.Slot-e.Point.Slot > slotThreshold) &&
146147
(headerCount+1) < allowedHeaderCount {
147148
return nil
148149
}
@@ -267,18 +268,59 @@ func (ls *LedgerState) calculateEpochNonce(
267268
return genesisHashBytes, nil
268269
}
269270
// Calculate stability window
270-
byronGenesis := ls.config.CardanoNodeConfig.ByronGenesis()
271271
shelleyGenesis := ls.config.CardanoNodeConfig.ShelleyGenesis()
272-
if byronGenesis == nil || shelleyGenesis == nil {
273-
return nil, errors.New("could not get genesis config")
272+
if shelleyGenesis == nil {
273+
return nil, errors.New("could not get Shelley genesis config")
274+
}
275+
var securityParam uint64
276+
// Use K parameter from Byron Genesis during Byron era, SecurityParam from Shelley Genesis for later eras
277+
if ls.currentEra.Id == 0 { // Byron era ID is 0
278+
byronGenesis := ls.config.CardanoNodeConfig.ByronGenesis()
279+
if byronGenesis == nil {
280+
return nil, errors.New("could not get Byron genesis config")
281+
}
282+
k := byronGenesis.ProtocolConsts.K
283+
if k < 0 {
284+
return nil, fmt.Errorf("k parameter must be non-negative: %d", k)
285+
}
286+
// Safe conversion: K is non-negative
287+
securityParam = uint64(k) // #nosec G115
288+
} else {
289+
secParam := shelleyGenesis.SecurityParam
290+
if secParam < 0 {
291+
return nil, fmt.Errorf("security param must be non-negative: %d", secParam)
292+
}
293+
// Safe conversion: SecurityParam is non-negative
294+
securityParam = uint64(secParam) // #nosec G115
295+
}
296+
// Calculate stability window: 3k/f for Shelley+ eras, just k for Byron
297+
var stabilityWindow uint64
298+
if ls.currentEra.Id == 0 { // Byron era
299+
stabilityWindow = securityParam
300+
} else {
301+
// Validate ActiveSlotsCoeff before using it
302+
if shelleyGenesis.ActiveSlotsCoeff.Rat == nil {
303+
return nil, errors.New("empty ActiveSlotsCoeff")
304+
}
305+
activeSlotsRat := shelleyGenesis.ActiveSlotsCoeff.Rat
306+
// Check for division by zero
307+
if activeSlotsRat.Num().Sign() == 0 {
308+
return nil, errors.New("divide by zero")
309+
}
310+
// Compute stability window: floor((3 * securityParam) / f)
311+
// Using integer arithmetic to avoid floating point precision issues
312+
numerator := new(big.Int).SetUint64(3 * securityParam)
313+
quotient := new(big.Int).Div(
314+
new(big.Int).Mul(numerator, activeSlotsRat.Denom()),
315+
activeSlotsRat.Num(),
316+
)
317+
318+
// Check if result fits in uint64
319+
if !quotient.IsUint64() {
320+
return nil, errors.New("stability window calculation result too large")
321+
}
322+
stabilityWindow = quotient.Uint64()
274323
}
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()
282324
var stabilityWindowStartSlot uint64
283325
if epochStartSlot > stabilityWindow {
284326
stabilityWindowStartSlot = epochStartSlot - stabilityWindow

0 commit comments

Comments
 (0)