Epoch totalSupply
is not properly updated retroactively
#91
Labels
type: bug
Something isn't working
Milestone
Here's an issue I ran into while writing/updating tests for #14:
UPDATE: I realized, while writing this down, it's essentially the same issue described in #48
The staking contracts keep track of two things - epochs and accounts.
Epochs can be advanced by calling
executeEpoch()
. This will calculate the overallpendingRewards
as well as theepochRewards
for the current epoch and thetotalSupply
of the current epoch.executeEpoch()
is also called via modifiers in any state changing operation (stake(),
unstake()` etc)Epochs can advance without stakers having calculated their epoch rewards. This is done so that when
executeEpoch()
is called, an account doesn't have to process the rewards for all accounts of the system, but only its own.In other words, it's very possible to have a state where, say, the
currentEpoch: 25
andaccountOneEpoch: 1
andaccountTwoEpoch: 10
. Every account has to take care of getting their rewards processed for every epoch.If accounts happen to not do that for any given epoch, then these will be part of the
pendingRewards
.Here's the problem
Because an account can process its rewards at a time when
currentEpoch
is higher than theaccountEpoch
, we need to update an epochstotalSupply
retroactively once the rewards for that account have been calculated.Right now this is not happening for all epochs between
accountEpoch + 1
andcurrentEpoch
.Concrete example:
currentEpoch: 0
totalSupply: 0
-totalSupply
is staking balance + MP balance of contract100
tokens)currentEpoch: 1
- epoch has advanced as any staking action progresses the epoch to the nextaccountEpoch: 1
epoch(1)TotalSupply: 200
- (again, stake balance + MP)currentEpoch: 2
accountEpoch: 1
epoch(1)TotalSupply: 200
- totalSupply hasn't changed yet for this epoch, because account processing hasn't happened yetepoch(2)TotalSupply: 0
- This will update once weexecuteEpoch
on epoch 2epochEnd
is reached andexecuteEpoch()
is calledcurrentEpoch: 3
accountEpoch: 1
epoch(1)totalSupply: 200
epoch(2)totalSupply: 200
epoch(3)totalSupply: 0
- same, this will update onceexecuteEpoch
is called againexecuteAccount(2)
- "2" is the number of the epoch to progress tocurrentEpoch: 4
- this has increased asexecuteEpoch
is called viaexecuteAccount
accountEpoch: 2
epoch(1)totalSupply: 250
- 250 is made up here, the point is that MP were minted so totalSupply goes up in epoch 1epoch(2)totalSupply: 200
epoch(3)totalSupply: 200
- this is now updated becauseexecuteAccount
callsexecuteEpoch
epoch(4)totalSupply: 0
Let's pause here for a second. Notice that the account has processed its "own" epoch 1, so it's now at epoch 2. Due to the processing, it has calculated its rewards and the
totalSupply
of epoch one has been updated accordingly.The totalSupply of the other epochs are left untouched, until the account processes its rewards for those epochs.
Let's continue:
executeAccount(3)
currentEpoch: 5
accountEpoch: 3
epoch(1)totalSupply: 250
epoch(2)totalSupply: 250
- Whoops, this doesn't look right. TotalSupply of this epoch should be higherepoch(3)totalSupply: 200
epoch(4)totalSupply: 200
epoch(5)totalSupply: 0
And this is where we run into underflow bugs.
When we
executeAccount()
we're always operating on the state of the current epoch. The moment we callexecuteAccount(3)
we're operating onepoch 2
which at this point in time, still only has thetotalSupply
without all the MP calculation that have happened in the meantime.So what actually needs to happen, is that when we caculate the rewards for a given account and a given epoch, we need to update all following epochs until the latest one as well.
This problem only gets more complex the more stakers enter the system.
Here are some more concrete logs that show the same issue:
The text was updated successfully, but these errors were encountered: