Skip to content

Commit f380e5c

Browse files
committed
Pulse Steps II
1 parent 42c400c commit f380e5c

File tree

1 file changed

+75
-6
lines changed

1 file changed

+75
-6
lines changed

docs/reward-calculation/HowRewardCalculationWorks.md

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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
419417
type 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
473471
To prevent a huge pause in the Reward Calculation we spread out some of the computation
474472
over 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
482480
2. 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`
492490
which can be used to update the `NewEpochState`
493491
494492
The 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.
499499
createRUpd ::
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

Comments
 (0)