@@ -414,8 +414,6 @@ is `RewardAns` which has two maps as components
414414
415415
416416```
417- -- | The result of reward calculation is a pair of aggregate Maps.
418- -- One for the accumulated answer, and one for the answer since the last pulse
419417type RewardEvent = Map (Credential 'Staking) (Set Reward)
420418
421419-- | The result of reward calculation is a pair of aggregate Maps.
@@ -468,7 +466,7 @@ instance Pulsable RewardPulser where
468466 pure $ VMap.foldlWithKey (rewardStakePoolMember free) ans balance
469467```
470468
471- ## The Phases of a pusling computation
469+ ## The Phases of a pulsing computation
472470
473471To prevent a huge pause in the Reward Calculation we spread out some of the computation
474472over many blocks in the idle time of a node. We do this in 3 steps
@@ -481,21 +479,23 @@ that must be held constant, and creating the thunks that will execute when it is
481479
4824802. The Pulse Step. Run the pulser for a bit. It returns a PulsingRewardUpdate, which is one of two things
483481 - The final result, if the pulser has completed
484- - Enough information to restart the pulser for the nex pulse
482+ - Enough information to restart the pulser for the next pulse
485483 ```
486484 data PulsingRewUpdate
487485 = Pulsing !RewardSnapShot !Pulser -- Pulsing work still to do
488486 | Complete !RewardUpdate -- Pulsing work completed, ultimate goal reached
489487 ```
490488
491- 3. The Complete Step. Clean up loose ends, and the return the `RewardUpdate`
489+ 3. The Complete Step. Clean up loose ends, and return the `RewardUpdate`
492490which can be used to update the `NewEpochState`
493491
494492The 3 steps are combined in this function
495493
496494```
497495-- | To create a reward update, run all 3 phases
498496-- This function is not used in the rules, so it ignores RewardEvents
497+ -- but it illustrates the three phases and the looping behavior of the second phase
498+ -- through a recursive call. It is also used in some tests.
499499createRUpd ::
500500 forall era.
501501 (EraGov era, EraCertState era) =>
@@ -512,4 +512,73 @@ createRUpd slotsPerEpoch blocksmade epstate maxSupply asc secparam = do
512512 case step2 of
513513 Complete r -> pure r
514514 Pulsing rewsnap pulser -> fst <$> completeRupd (Pulsing rewsnap pulser)
515- ```
515+ ```
516+
517+ In the STS system, the three phases are packed into one transition funtion `rupdTransisiton`
518+ which is called on every tick in the STS TICK rule. The three phases
519+ are ordered by observing the current slot and returning a `RewardTiming` value
520+
521+
522+ ```
523+ -- | The Goldilocks labeling of when to do the reward calculation.
524+ data RewardTiming = RewardsTooEarly | RewardsJustRight | RewardsTooLate
525+
526+ determineRewardTiming :: SlotNo -> SlotNo -> SlotNo -> RewardTiming
527+ determineRewardTiming currentSlot startAfterSlot endSlot
528+ | currentSlot > endSlot = RewardsTooLate
529+ | currentSlot <= startAfterSlot = RewardsTooEarly
530+ | otherwise = RewardsJustRight
531+ ```
532+
533+ 1. A Timing of `RewardsToEarly` is a No-op in the TickRule where pulsing is concerned
534+ 2. A Timing of `RewardsJustRight` means take a StartStep, or a PulseStep
535+ 3. A Timing of `RewardsTooLate` means no time is left, so the rest of the pulsing is forced.
536+
537+ ```
538+ rupdTransition :: (EraGov era, EraCertState era) => TransitionRule (ShelleyRUPD era)
539+ rupdTransition = do
540+ TRC (RupdEnv b es, ru, s) <- judgmentContext
541+ (slotsPerEpoch, slot, slotForce, maxLL, asc, k, e) <- liftSTS $ do
542+ ei <- asks epochInfoPure
543+ sr <- asks randomnessStabilisationWindow
544+ let e = epochInfoEpoch ei s
545+ slotsPerEpoch = epochInfoSize ei e
546+ slot = epochInfoFirst ei e +* Duration sr
547+ maxLL <- asks maxLovelaceSupply
548+ asc <- asks activeSlotCoeff
549+ k <- asks securityParameter -- Maximum number of blocks we are allowed to roll back
550+ return (slotsPerEpoch, slot, slot +* Duration sr, maxLL, asc, k, e)
551+ let maxsupply = Coin (fromIntegral maxLL)
552+ case determineRewardTiming s slot slotForce of
553+ -- Waiting for the stability point, do nothing, keep waiting
554+ RewardsTooEarly -> pure SNothing
555+ -- More blocks to come, get things started or take a step
556+ RewardsJustRight ->
557+ case ru of
558+ SNothing ->
559+ -- This is the first opportunity to pulse, so start pulsing.
560+ -- SJust <$> tellLeaderEvents (e + 1) (fst (startStep slotsPerEpoch b es maxsupply asc k))
561+ (pure . SJust) (startStep slotsPerEpoch b es maxsupply asc k)
562+ (SJust p@(Pulsing _ _ )) -> do
563+ -- We began pulsing earlier, so run another pulse
564+ (ans, event) <- liftSTS $ pulseStep p
565+ tellRupd "Pulsing Rupd" (RupdEvent (succ e) event)
566+ pure (SJust ans)
567+ (SJust p@(Complete _ )) -> pure (SJust p)
568+ -- Time to force the completion of the pulser so that downstream tools such as db-sync
569+ -- have time to see the reward update before the epoch boundary rollover.
570+ RewardsTooLate ->
571+ case ru of
572+ SNothing -> do
573+ -- Nothing has been done, so start, and then complete the pulser. We hope this is very rare.
574+ let pulser = startStep slotsPerEpoch b es maxsupply asc k
575+ (reward, event) <- liftSTS $ completeStep pulser
576+ tellRupd "Starting too late" (RupdEvent (succ e) event)
577+ pure (SJust reward)
578+ SJust p@(Pulsing _ _ ) -> do
579+ -- We have been pulsing, but we ran out of time, so complete the pulser.
580+ (reward, event) <- liftSTS $ completeStep p
581+ tellRupd "Completing too late" (RupdEvent (succ e) event)
582+ pure (SJust reward)
583+ complete@(SJust (Complete _ )) -> pure complete
584+ ```
0 commit comments