diff --git a/tips/TIP-0040/assets/selection-timing.png b/tips/TIP-0040/assets/selection-timing.png
old mode 100755
new mode 100644
index 7646dd043..e37d733a6
Binary files a/tips/TIP-0040/assets/selection-timing.png and b/tips/TIP-0040/assets/selection-timing.png differ
diff --git a/tips/TIP-0040/tip-0040.md b/tips/TIP-0040/tip-0040.md
index 3e78007cf..490ae6c9c 100644
--- a/tips/TIP-0040/tip-0040.md
+++ b/tips/TIP-0040/tip-0040.md
@@ -73,7 +73,7 @@ Protocol parameters used throughout this TIP are defined in [TIP-49](../TIP-0049
## Common Parameters and Functions
-- Let `decay(value, n)` be calculated with the algorithm and lookup table in [TIP-39](../TIP-0039/tip-0039.md).
+- Let `Decay(value, epochIndexDiff)` be defined as in [TIP-39 (Decay Function)](../TIP-0039/tip-0039.md#decay-function).
# Staking
@@ -102,15 +102,16 @@ After an epoch ends, the delegators of the pool can claim their rewards. The val
stake by going through an unbonding period of its locked tokens. After this period ends, the validator can unlock their
IOTA coins and claim their Mana rewards.
-## Registration
+## Validator Registration
Accounts are considered registered as committee candidates for epoch `Candidate Epoch` if they satisfy all of the
following conditions:
- The account has a [_Staking Feature_](../TIP-0042/tip-0042.md#staking-feature) in their _Features_ where
`End Epoch >= Candidate Epoch`.
-- The account issues a block with a _Candidacy Announcement Payload_ before/or in (TODO: Clarify) the
- `Registration Slot(Candidate Epoch)` as defined in [committee selection](#committee-selection).
+- The account issues a Basic Block in a slot `Block Slot` with a _Candidacy Announcement Payload_ where
+ `Start Slot(Candidate Epoch - 1) <= Block Slot <= Registration Slot(Candidate Epoch)`. `Epoch Start Slot` and
+ `Registration Slot` are defined in [committee selection](#committee-selection).
### Candidacy Announcement
@@ -150,38 +151,35 @@ windows is not accurately depicted here and instead depends on the value of the
The following explains how the committee selection for a certain epoch `n` works, with the following time boundaries
used in the algorithm:
-- Let `Epoch Start Slot(n))` be the first slot index of epoch `n`.
-- Let `Registration Slot(n)` be the registration slot with index
- `Epoch Start Slot(n) - Epoch Nearing Threshold - Activity Window Duration - 1`. To be eligible for the committee
- selection of epoch `n`, a potential validator must register until this slot.
-- Let `Activity Window Slot(n)` be the slot of `Epoch Start Slot(n) - Epoch Nearing Threshold - 1`. To be eligible for
- the committee selection of epoch `n`, a potential validator must issue at least one block between
- `Registration Slot(n)`+1. and this slot.
+- Let `Epoch Start Slot(n)` be the first slot index of epoch `n`.
+- Let `Registration Slot(n)` be the registration slot with index `Epoch Start Slot(n) - Epoch Nearing Threshold - 1`. To
+ be eligible for the committee selection of epoch `n`, a potential validator must register until this slot, as defined
+ in [validator registration](#validator-registration).
- Let `Cutoff Slot(n)` be the slot with index `Epoch Start Slot(n) - Maximum Committable Age - 1`. This slot is used in
checks to decide whether the committee will be rotated or not in the epoch.
The committee will be a subset of the active _registered validators_. To define this subset, the following steps are
taken:
-- The set of _registered validators_ is defined as all Account Outputs that satisfy all of the following conditions:
- - The account has a _Staking Feature_ at the end of the `Registration Slot(n)`.
- - The account's Staking Feature's `End Epoch` is greater or equal to `n`.
-- For any validator `i` in the set of _registered validators_ the activity is determined. The validator is active if the
- following condition holds; otherwise, it is inactive:
- - The account with `Account ID` issues at least one block whose `Account ID` field is equal to that of the account,
- and the block's `Issuing Time` field converted to a slot index `Block Slot Index`, satisfies all of the following
- conditions:
- - `Block Slot Index > Registration Slot(n)`.
- - `Block Slot Index <= Activity Window Slot(n)`.
+- The set of _registered validators_ is defined as all Account Outputs that
+ [registered for Epoch n](#validator-registration).
- For any validator `i` in the set of _registered validators_ the pool stake is equal to `Stake_i + DelegatedStake_i` at
the end of the slot with index `Registration Slot(n)`, where:
- - `Stake_i` is the `Staked Amount` of IOTA coins of the validator's _Staking Feature_.
- - `DelegatedStake_i` is the sum of all _Delegation Output's_ `Amount` field where the value of the `Validator ID`
- field is equal to `i`.
-- The set of _registered validators_ who are active constitute the set of _eligible validators_, which is sorted by pool
- stake in descending order. The first `Committee Size` entries in the _eligible validators_ set is Epoch `n`'s
- preliminary committee.
-- The preliminary committee becomes the selected committee if the `Activity Window Slot(n)` is finalized before the
+ - `Stake_i` is the `Staked Amount` of the validator's _Staking Feature_.
+ - `DelegatedStake_i` is the sum of all _Delegation Output's_ `Amount` field where the value of the `Validator Address`
+ field is equal to the Account Address of `i`.
+- The set of _registered validators_ is sorted as follows and with decreasing priority in the sort condition. To compare
+ two validators from the set:
+ - If pool stakes are not equal, sort by pool stake in descending order.
+ - If validator stakes are not equal, sort by validator stake in descending order.
+ - If stake end epochs are not equal, sort by stake end epoch in descending order.
+ - If fixed costs are not equal, sort by fixed cost in ascending order.
+ - Otherwise sort by Account ID in descending order.
+ - Since two validators never have the same Account ID, this ensures a selection criteria as a last resort.
+- The size of the committee `Committee Size` is set to the minimum of the `Target Committee Size` and the number of
+ registered validators.
+- The first `Committee Size` entries in the _registered validators_ set is Epoch `n`'s preliminary committee.
+- The preliminary committee becomes the selected committee if the `Registration Slot(n)` is finalized before the
`Cutoff Slot(n)` is committed; otherwise, the committee of the current epoch becomes the selected committee of the
next epoch.
@@ -400,8 +398,8 @@ the epoch ends, that is, when its last slot is committed.
For each slot, the performance factor of each validator in the selected committee must be tracked. For an epoch, this
results in a list of performance factors for a validator. The rewards for an ongoing epoch are tracked by keeping a map
-with `Validator ID`s as keys, of type `ByteArray[32]`, and the values being a list of objects consisting of these
-fields:
+of slot indices to a map of the validator's activity in that slot, identified by the `Validator ID`. The activity is
+thus tracked per slot, per validator with objects consisting of these data:
@@ -410,58 +408,61 @@ fields:
Description |
- Slot Index |
- uint64 |
- The slot index for which the performance factor is tracked. |
+ Slot Activity Vector |
+ uint32 |
+ A bitmap where each bit corresponds to a subslot in the slot for which the performance factor is tracked. Hence, the number of subslots (equal to Validation Blocks Per Slot ) cannot be greater than 32. |
- Performance Factor |
+ Blocks Issued Count |
uint8 |
- The slot performance factor of the validator, scaled to TargetValidationBlocks. |
+ The total number of blocks issued by the validator in the slot. |
-### Performance Factor
+### Slot Performance Factor
This performance factor for a slot is defined as follows.
#### Input values
-- Let `Validation Blocks Issued` be the array of _Validation Blocks_ issued by a given validator in a certain slot.
-- Let `TargetValidationBlocks` be a protocol parameter representing the number of validation blocks a validator is
- supposed to issue per slot. Note that, when using an `uint8` to store `Performance Factor`, `TargetValidationBlocks`
- must be strictly smaller than `2^8`, otherwise the performance of a well behaved validator would overflow the
- variable.
+- Let `Validation Blocks Per Slot` be a protocol parameter representing the number of validation blocks a validator is
+ supposed to issue per slot. `Validation Blocks Per Slot` must be less or equal than 32 in order for all subslots to
+ fit into the `Slot Activity Vector` bitmap.
#### Calculations
-- First, divide the slot representing times `[beginningSlot, endSlot)` into `TargetValidationBlocks` subslots of length
- `subslotLength = slotDuration/TargetValidationBlocks` given by
- `[beginningSlot+(i-1)*subslotLength, beginningSlot+i*subslotLength)`, for `i=1,...,TargetValidationBlocks`.
-- Then, count how many of these subslots have at least one validation block issued in them. In principle, this will be
- the `Slot Performance Factor`.
-- Check if the total number of validation blocks issued in this slot is larger than `TargetValidationBlocks`. In the
- case that `TargetValidationBlocks` is strictly smaller than `2^8-1`, one could set `Slot Performance Factor` to
- `TargetValidationBlocks + 1`, to signal that the validator issued more validation blocks than it was supposed to and
- it should be punished when combining the `Slot Performance Factors` into `Epoch Performance Factors`. In the case that
- `TargetValidationBlocks` is larger or equal than `2^8-1`, this "trick" can overflow the variables used.
+- Divide the slot representing times `[Slot Start, Slot End)` into `Validation Blocks Per Slot` subslots of length
+ `Subslot Duration = Slot Duration In Seconds/Validation Blocks Per Slot` given by
+ `[Slot Start + (i - 1) * Subslot Duration, Slot Start + i * Subslot Duration)`, for
+ `i = 1,...,Validation Blocks Per Slot`.
+- Determine how many of these subslots have at least one validation block issued in them and set the bit with the
+ subslot index in the `Slot Activity Vector`. This is the `Slot Performance Factor`.
+- Count the number of validation blocks issued in this slot as `Blocks Issued Count`. This value should be capped at
+ `Validation Blocks Per Slot`. If the number of blocks issued in this slot is greater than
+ `Validation Blocks Per Slot`, set `Blocks Issued Count` to `Validation Blocks Per Slot + 1` to signal that the
+ validator issued more validation blocks than it was supposed to and that it should be punished when combining the slot
+ performance factors into an epoch performance factor.
+ - Since `Validation Blocks Per Slot` must be less or equal than 32, this approach will not overflow
+ `Blocks Issued Count`.
## Epoch Level
-### Epoch performance factor
+### Epoch Performance Factor
#### Input values
-- Let `Slot Performance Factor Array` be the array of _Slot Performance Factors_ of a given validator in each slot on an
+- Let `Slot Performance Factor Array` be the array of _Slot Performance Factors_ of a given validator in each slot of an
epoch.
#### Calculations
-- Then `Performance Factor` is given by the average between these values, rounded down to the nearest integer.
- Additionally, if the validator issued more than `TargetValidationBlocks` validation blocks in any slot, the
- performance factor for the whole epoch is set to 0 as a form of punishment.
-
-Note that the value of the `Performance Factor` will be an integer between 0 and `TargetValidationBlocks`.
+- Then `Epoch Performance Factor` is given by the average between these values, rounded down to the nearest integer.
+ This can be calculated as the sum of bits set to `1` in each `Slot Activity Vector` with a subsequent _zero-fill
+ right-shift_ operation by `Slots Per Epoch Exponent`.
+- If the validator issued more than `Validation Blocks Per Slot` validation blocks in any slot, that is, if
+ `Blocks Issued Count > Validation Blocks Per Slot`, then `Epoch Performance Factor` is set to 0 as a form of
+ punishment.
+ - Note that the value of the `Epoch Performance Factor` will be an integer between 0 and `Validation Blocks Per Slot`.
### Profit Margins
@@ -473,16 +474,16 @@ Note that the value of the `Performance Factor` will be an integer between 0 and
#### Parameters
-- Let `ProfitMarginExponent` be 8.
+- Let `Profit Margin Exponent` be 8.
#### Calculations
- Given the values defined above, then
- `Profit Margin = (Total Validator Stake<2Profit Margin Exponent, so `Profit Margin` can
+ be set to use `Profit Margin Exponent` bits.
-Additionally, the profit margins must be tracked for each epoch.
+The `Profit Margin` must be tracked for each epoch:
@@ -490,26 +491,19 @@ Additionally, the profit margins must be tracked for each epoch.
Type |
Description |
-
- Epoch Index |
- uint64 |
-
- The epoch index for which the profit margin is tracked.
- |
-
Profit Margin |
uint8 |
- An integer representing the epoch profit margin, scaled to `2^ProfitMarginExponent`.
+ An integer representing the epoch profit margin, scaled to 2Profit Margin Exponent .
|
### Epoch Rewards
-The rewards of an epoch are tracked by keeping a map with `Validator ID`s as keys, of type `ByteArray[32]`, and the
-values being a list of objects consisting of these fields:
+The rewards of an epoch are tracked by keeping a map from epoch indices to a map from `Validator ID`s to values
+consisting of these fields:
@@ -517,11 +511,6 @@ values being a list of objects consisting of these fields:
Type |
Description |
-
- Epoch Index |
- uint64 |
- The epoch index for which the rewards can be claimed. |
-
Pool Stake |
uint64 |
@@ -532,6 +521,11 @@ values being a list of objects consisting of these fields:
uint64 |
The total amount of rewards the validator pool received. |
+
+ Fixed Cost |
+ uint64 |
+ The fixed cost of the validator in that epoch. |
+
The next section defines how these values can be obtained from the slot-level data, after the commitment of the last
@@ -544,18 +538,17 @@ The following section specifies how to convert the slot-level data into epoch-le
#### Parameters needed for calculations:
- `Bootstrapping Duration` = 1154 (approximately 3 years)
-- `Rewards Mana Share Coefficient` = 2 (relative to the term $\theta/(1-\theta)$ from the Whitepaper, with $\theta =
- 2/3$)
-- `Final Reward` =
- `(Total Supply * Rewards Mana Share Coefficient * generationRate) >> (generationRateExponent - slotsPerEpochExponent)`
- (constant reward per epoch after the bootstrapping phase)
-- `Decay Balancing Constant Coefficient` = 8
-- `Decay Balancing Constant =` (integer approximation of $2^{\text{Decay Balancing Constant Coefficient}}\frac{e
+- `Mana Share Coefficient` = 2 (relative to the term $\theta/(1-\theta)$ from the _IOTA 2.0 Incentives and Tokenomics
+ Whitepaper_, with $\theta = 2/3$)
+- `Final Reward = (Total Supply * Mana Share Coefficient * Generation Rate) >> (Generation Rate Exponent - Slots Per Epoch Exponent)`.
+ - This is the constant reward per epoch after the bootstrapping phase.
+- `Decay Balancing Constant` is an integer approximation of $2^{\text{Decay Balancing Constant Coefficient}}\frac{e
\Delta}{T(1-\exp{(-\beta \Delta)})}$, for $Delta =$ epoch duration in years, $T=$ `Bootstrapping Duration`, and
- $\beta=1/3$). For `Decay Balancing Constant Coefficient` = 8, `Decay Balancing Constant` = 696.
-- `Initial Reward` = `(Final Reward * Decay Balancing Constant) >> Decay Balancing Constant Coefficient` (parameter to
- calculate the reward per epoch in the bootstrapping phase)
-- `Pool Coefficient Exponent` = 11
+ $\beta=1/3$.
+ - This is the parameter to calculate the reward per epoch in the bootstrapping phase.
+- For `Decay Balancing Constant Coefficient = 8`, `Decay Balancing Constant = 696`.
+- `Initial Reward = (Final Reward * Decay Balancing Constant) >> Decay Balancing Constant Coefficient`.
+- `Pool Coefficient Exponent = 11`.
#### Input values
@@ -570,21 +563,23 @@ The following section specifies how to convert the slot-level data into epoch-le
#### Calculations
- The total target reward `Target Reward(n)` for an epoch index `n` is defined as:
- - `Initial Reward * decay(n)` if `n <= Bootstrapping Duration`.
+ - `Initial Reward * Decay(n)` if `n <= Bootstrapping Duration`.
- `Final Reward` if `n > Bootstrapping Duration`.
- The Pool Reward `Pool Reward` for an epoch index `n`, and pool `i` is calculated as follows:
- Let `Pool Coefficient` be
`((Pool Stake << Pool Coefficient Exponent)/Total Stake) + (Validator Stake(i) << Pool Coefficient Exponent) / Total Validator Stake`.
- Notice that, since both `Pool Stake` and `Validator Stake(i)` use at most 53 bits of the variable, to not overflow
- the calculation, `Pool Coefficient Exponent` must be at most 11. `Pool Coefficient` will then use at most
- `Pool Coefficient Exponent + 1` bits.
- - Take the `Performance Factor` for the whole epoch `n` according to [Performance Factor](#performance-factor)
- - Let `Scaled Pool Reward` be `Pool Coefficient * Target Reward(n) * Performance Factor`. Since `Pool Coefficient`
- uses at most 12 bits, `Target Reward(n)` uses at most 41 bits, and `Performance Factor` uses at most 8 bits, this
- multiplication will not overflow using uint64 variables.
- - Finally, `Pool Reward` will be `((Scaled Pool Reward/TargetValidationBlocks) >> (Pool Coefficient Exponent+1))`.
- Note that if this value is smaller than `Fixed Cost`, the validator will not receive rewards from this epoch. This
- is done to incentivize validators to define reasonable values for their fixed cost.
+ - Since both `Pool Stake` and `Validator Stake(i)` use at most 53 bits of the variable, to not overflow the
+ calculation, `Pool Coefficient Exponent` must be at most 11. `Pool Coefficient` will then use at most
+ `Pool Coefficient Exponent + 1` bits.
+ - Take the `Epoch Performance Factor` for the whole epoch `n` according to
+ [Epoch Performance Factor](#epoch-performance-factor).
+ - Let `Scaled Pool Reward` be `Pool Coefficient * Target Reward(n) * Epoch Performance Factor`.
+ - Since `Pool Coefficient` uses at most 12 bits, `Target Reward(n)` uses at most 41 bits, and
+ `Epoch Performance Factor` uses at most 8 bits, this multiplication will not overflow using uint64 variables.
+ - Finally, `Pool Reward` will be
+ `((Scaled Pool Reward/Validation Blocks Per Slot) >> (Pool Coefficient Exponent + 1))`. Note that if this value is
+ smaller than `Fixed Cost`, the validator will not receive rewards from this epoch. This is done to incentivize
+ validators to define reasonable values for their fixed cost.
## Validator Rewards
@@ -594,89 +589,101 @@ the amount of Mana Rewards that can be claimed is defined as follows.
#### Parameters
-`ProfitMarginExponent` = 8
+- Let `Profit Margin Exponent` be 8.
#### Input Values
- Let `Profit Margin(n)` be the `Profit Margin` for epoch index `n`.
- Let `Pool Rewards(n)` be the `Pool Rewards` of the entry in `Claimable Rewards` with epoch index `n`.
- Let `Pool Stake(n)` be the `Pool Stake` of the entry in `Claimable Rewards` with epoch index `n`.
-- Let `Fixed Cost` be the `Fixed Cost` defined in the _Staking Feature_.
+- Let `Fixed Cost(n)` be the `Fixed Cost` defined in the _Staking Feature_ at epoch `n`.
#### Calculations
+- Calculate the rewards of a validator at epoch `n` as `Rewards(Decay End Epoch, n)`:
+ - Calculate the `Undecayed Rewards(n)` for a given epoch `n`.
+ - If `Pool Rewards(n) - Fixed Cost(n) < 0`, then `Undecayed Rewards(n)` is zero.
+ - If `Pool Rewards(n) - Fixed Cost(n) >= 0`, then `Undecayed Rewards(n)` is
+ `Fixed Cost(n) + Profit Margin Factor + Residual Validator Factor`, where:
+ - `Profit Margin Complement = (1 << Profit Margin Exponent) - Profit Margin(n)`.
+ - `Profit Margin Factor = (Profit Margin(n) * (Pool Rewards(n) - Fixed Cost(n))) >> Profit Margin Exponent`.
+ - `Residual Validator Factor = ((Profit Margin Complement * (Pool Rewards(n) - Fixed Cost(n))) >> Profit Margin Exponent) * Staked Amount/Pool Stake(n)`.
+ - Note that `((Profit Margin Complement * Pool Rewards(n)) >> Profit Margin Exponent) * Staked Amount` uses up
+ to 94 bits and `Pool Stake(n)` up to 53, so to prevent overflowing the first factor should be either stored as
+ a 128 bits integer or it should be stored as 2 `uint64` variables (and the proper 128 by 64 division algorithm
+ must be used).
+ - `Rewards(Decay End Epoch, n)` is the decayed reward for epoch `n`, that is:
+ - `Rewards(Decay End Epoch, n) = Decay(Undecayed Rewards(n), Decay End Epoch - n)`.
- Let `Future Bounded Slot Index` be given by `Commitment Index + Min Committable Age` where `Commitment Index` is the
slot index of the commitment input. If no _Commitment Input_ is present, the transaction is invalid.
- Note that the presence of a _Commitment Input_ is already required for any transaction containing a _Staking
Feature_ on the input or output side.
- Let `Future Bounded Epoch Index` be the epoch index corresponding to the slot index `Future Bounded Slot Index`.
-- Let the `Claimable Rewards` be all reward entries for the validator identified by the Account ID of the account that
- removes the Staking Feature, where the entry's `Epoch Index` satisfies `Epoch Index >= Start Epoch + 1` and
- `Epoch Index < Future Bounded Epoch Index`.
-- Let the total claimable rewards at epoch `m` be the sum of `Rewards(n)` for a certain set of epoch indexes `n> ProfitMarginExponent`
- - `Residual Validator Factor = ((Profit Margin Complement * (Pool Rewards(n)-Fixed Cost)) >> ProfitMarginExponent) * Staked Amount/Pool Stake(n)`
- - Note that `((Profit Margin Complement * Pool Rewards(n)) >> ProfitMarginExponent) * Staked Amount` uses up to 94
- bits and `Pool Stake(n)` up to 53, so to prevent overflowing the first factor should be either stored as a 128
- bits integer or it should be stored as 2 uint64 variables (and the proper 128 by 64 division algorithm must be
- used). Otherwise (i.e., if `Pool Rewards(n)-Fixed Cost<0`), `Undecayed Rewards(n)` must be set to zero.
-- Finally, decay the rewards according to [Mana Decay](../TIP-0039/tip-0039.md#mana-decay), meaning that
- `Rewards(n)=decay(Undecayed Rewards(n),m-n)`, where the function `decay` is defined in
- [TIP-39](../TIP-0039/tip-0039.md). The total claimable decayed reward for validator `i` will be, then, the sum of
- `Rewards(n)` for n in the validation period.
+- Let the `Claimable Rewards(id)` be all reward entries for the validator identified by the Account ID `id` of the
+ account that removes the Staking Feature, where the reward entry's epoch index `Epoch Index` satisfies all of the
+ following conditions:
+ - `Epoch Index >= Start Epoch + 1`.
+ - `Epoch Index < Last Reward Epoch` where `Last Reward Epoch` is the minimum of `Future Bounded Epoch Index` and
+ `End Epoch + 1`.
+- The total claimable decayed rewards for a validator `id` is the sum of `Rewards(Decay End Epoch, n)` for each epoch
+ `n` in `Claimable Rewards(id)` where `Decay End Epoch` is the maximum of `Future Bounded Epoch Index - 1` (or `0` if
+ that would underflow) and `Last Reward Epoch`.
+ - Note: The `- 1` is applied to allow claiming the full, undecayed reward for an epoch `n - 1` in epoch `n`.
## Delegation Rewards
To claim rewards, a _Delegation Output_ must be destroyed. Depending on the state it is in at that point, different
conditions for claiming rewards apply. An output destroyed in _Delegating State_ will always forfeit _potential_ rewards
-for the epoch in which it is destroyed, since the rewards for that epoch only become available in the next epoch. They
-are _potential_ since the validator to which the output is delegating may not have been selected into the committee for
-that epoch.
+for the epoch in which it is destroyed, since the rewards for that epoch only become available in the subsequent epoch.
+They are _potential_ since the validator to which the output is delegating may not have been selected into the committee
+for that epoch.
#### Parameters
-`ProfitMarginExponent` = 8 and `DecayExponent` = 32
+- Let `Profit Margin Exponent` be 8.
#### Input Values
- Let `Profit Margin(n)` be the `Profit Margin` for epoch index `n`.
-- Let `Pool Rewards(n)` be the `Pool Rewards` of the entry in `Claimable Rewards` with epoch index `n`.
-- Let `Pool Stake(n)` be the `Pool Stake` of the entry in `Claimable Rewards` with epoch index `n`.
-- Let `Fixed Cost` be the `Fixed Cost` of the Validator, as defined in its Staking Feature.
+- Let `Pool Rewards(n)` be the `Pool Rewards` of the entry in `Claimable Rewards(id)` with epoch index `n` for validator
+ `id`.
+- Let `Pool Stake(n)` be the `Pool Stake` of the entry in `Claimable Rewards(id)` with epoch index `n` for validator
+ `id`.
+- Let `Fixed Cost(n)` be the `Fixed Cost` defined in the _Staking Feature_ at epoch `n`.
#### Calculations
The amount of Mana Rewards that can be claimed for a _Delegation Output_ which is destroyed is defined as follows.
-- Let the `Claimable Rewards` be all reward entries for the validator identified by the Account ID `Validator ID` field
- in the output, where its `Epoch Index` satisfies `Epoch Index >= Start Epoch` and `Epoch Index <= Delegation End`,
- where:
+- Calculate the rewards of a delegator at epoch `n` as `Rewards(Decay End Epoch, n)`:
+ - Calculate the `Undecayed Rewards(n)` for a given epoch `n`.
+ - If `Pool Rewards(n) - Fixed Cost(n) >= 0`, then `Undecayed Rewards(n)` is
+ `((Profit Margin Complement * (Pool Rewards(n) - Fixed Cost(n))) >> Profit Margin Exponent) * Delegated Amount/Pool Stake(n)`.
+ - Note that
+ `((Profit Margin Complement * (Pool Rewards(n) - Fixed Cost(n))) >> Profit Margin Exponent) * Delegated Amount`
+ uses up to 94 bits and `Pool Stake(n)` up to 53, so to prevent overflowing the first factor should be either
+ stored as a 128 bits integer or it should be stored as 2 uint64 variables (and the proper 128 by 64 division
+ algorithm must be used).
+ - If `Pool Rewards(n) - Fixed Cost(n) < 0`, then `Undecayed Rewards(n)` is
+ `((Profit Margin Complement * Pool Rewards(n)) >> Profit Margin Exponent) * Delegated Amount/Pool Stake(n)`.
+ - `Rewards(Decay End Epoch, n)` is the decayed reward for epoch `n`, that is:
+ - `Rewards(Decay End Epoch, n) = Decay(Undecayed Rewards(n), Decay End Epoch - n)`.
+- Let the `Claimable Rewards(address)` be all reward entries for the validator identified by the Account ID
+ corresponding to the `address`, which is the `Validator Address` field in the Delegation Output, where the entry's
+ epoch index `Epoch Index` satisfies `Epoch Index >= Start Epoch` and `Epoch Index <= Delegation End`, where:
- If the output is in _Delegating State_ let `Delegation End` be `Future Bounded Epoch Index - 1`, where:
- `Future Bounded Slot Index` is given by `Commitment Index + Min Committable Age` where `Commitment Index` is the
slot index of the commitment input. If no _Commitment Input_ is present, the transaction is invalid.
- Note that the presence of a _Commitment Input_ is already required for any transaction containing a _Delegation
Output_ on the input or output side, except when it is destroyed in the _Delayed Claiming State_.
- `Future Bounded Epoch Index` is the epoch index corresponding to the slot index `Future Bounded Slot Index`.
- - Note that no transaction validation rule exists to prevent claiming rewards before the rewards of the previous
- epoch became available, thus forfeiting the potential rewards of that previous epoch.
+ - Note that no transaction validation rule exists to prevent claiming rewards too early, that is, before the rewards
+ of the previous epoch became available, thus forfeiting the potential rewards of that previous epoch.
- If the output is in _Delayed Claiming State_ let `Delegation End` be `End Epoch`.
-
-Let the total claimable rewards be the sum of `Rewards(n)` for an epoch index `n`. Calculate the undecayed rewards for
-each epoch `n` `Undecayed Rewards(n)`. In the case where `Pool Rewards(n)-Fixed Cost≥0`, the following holds:
-`Undecayed Rewards(n)` =
-`((Profit Margin Complement * (Pool Rewards(n)-Fixed Cost)) >> ProfitMarginExponent) * Delegated Amount/Pool Stake(n)`.
-Note that `((Profit Margin Complement * (Pool Rewards(n)-Fixed Cost)) >> ProfitMarginExponent) * Delegated Amount` uses
-up to 94 bits and `Pool Stake(n)` up to 53, so to prevent overflowing the first factor should be either stored as a 128
-bits integer or it should be stored as 2 uint64 variables (and the proper 128 by 64 division algorithm must be used).
-Otherwise (i.e., if `Pool Rewards(n)-Fixed Cost<0`), set the undecayed rewards as `Undecayed Rewards(n)` =
-`((Profit Margin Complement * Pool Rewards(n)) >> ProfitMarginExponent) * Delegated Amount/Pool Stake(n)`. Then, decay
-the rewards according to [Mana Decay](../TIP-0039/tip-0039.md#mana-decay), meaning that
-`Rewards(n)=decay(Undecayed Rewards(n),m-n)`. The total claimable decayed reward for validator `i` will then be the sum
-of `Rewards(n)` for n in the validation period.
+- The total claimable decayed rewards for a Delegation Output with `address` set to the `Validator Address`, be the sum
+ of `Rewards(Decay End Epoch, n)` for each epoch `n` in `Claimable Rewards(address)`, where `Decay End Epoch` is the
+ maximum of `Future Bounded Epoch Index - 1` (or `0` if that would underflow) and `Delegation End`.
+ - Note: The `- 1` is applied to allow claiming the full, undecayed reward for an epoch `n - 1` in epoch `n`.
# Rationale & Design