Reward distribution

Namada uses the automatically-compounding variant of F1 fee distribution.

Rewards are given to validators for proposing blocks, for voting on finalizing blocks, and for being in the consensus validator set: the funds for these rewards can come from minting (creating new tokens). The amount that is minted depends on how many staking tokens are locked (staked) and some maximum annual inflation rate. The rewards mechanism is implemented as a PD controller that dynamically adjusts the inflation rate to achieve a target staking token ratio. When the total fraction of tokens staked is very low, the return rate per validator needs to increase, but as the total fraction of stake rises, validators will receive fewer rewards. Once the desired staking fraction is achieved, the amount minted will just be the desired annual inflation.

Each delegation to a validator is initiated at an agreed-upon commission rate charged by the validator. Validators pay out rewards to delegators based on this mutually-determined commission rate. The minted rewards are auto-bonded and only transferred when the funds are unbonded. Once the protocol determines the total amount of tokens to mint at the end of the epoch, the minted tokens are effectively divided among the relevant validators and delegators according to their proportional stake. In practice, the reward products, which are the fractional increases in staked tokens claimed, are stored for the validators and delegators, and the reward tokens are only transferred to the validator’s or delegator’s account upon withdrawal. This is described in the following sections. The general system is similar to what Cosmos does.

Basic algorithm

Consider a system with

  • a canonical singular staking unit of account.
  • a set of validators .
  • a set of delegations , where indicates the associated validator, each with a particular initial amount.
  • epoched proof-of-stake, where changes are applied as follows:
    • bonding is processed after the pipeline length
    • unbonding is processed after the pipeline + unbonding length
    • rewards are paid out at the end of each epoch, i.e., in each epoch , a reward is paid out to validator
    • slashing is applied as described in slashing.

We wish to approximate as exactly as possible the following ideal delegator reward distribution system:

  • At each epoch, for a validator , iterate over all of the delegations to that validator. Update each delegation , as follows. where and respectively denote the reward and stake of validator at epoch .
  • Similarly, multiply the validator's voting power by the same factor , which should now equal the sum of their revised-amount delegations.

In this system, rewards are automatically rebonded to delegations, increasing the delegation amounts and validator voting powers accordingly.

However, we wish to implement this without actually needing to iterate over all delegations each block, since this is too computationally expensive. We can exploit this constant multiplicative factor , which does not vary per delegation, to perform this calculation lazily. In this lazy method, only a constant amount of data per validator per epoch is stored, and revised amounts are calculated for each individual delegation only when a delegation changes.

We will demonstrate this for a delegation to a validator . Let denote the stake of at epoch .

For two epochs and with , define the function as

Denote as . The function has a useful property.

One may calculate the accumulated changes upto epoch as

If we know the delegation upto epoch , the delegation at epoch is obtained by the following formula,

Using property ,

Clearly, the quantity does not depend on the delegation . Thus, for a given validator, we only need to store this product at each epoch , from which the updated amounts for all delegations can be calculated.

The product at the end of each epoch is updated as follows.


updateProducts 
:: HashMap<Address, HashMap<Epoch, Float>> 
-> HashSet<Address> 
-> Epoch 
-> HashMap<BondId, Token::amount>>

updateProducts validatorProducts activeSet currentEpoch = 
 let stake = PoS.readValidatorTotalDeltas validator currentEpoch
     reward = PoS.reward stake currentEpoch
     rsratio = reward / stake
     entries = lookup validatorProducts validator
     lastProduct = lookup entries (Epoch (currentEpoch - 1))
 in insert currentEpoch (lastProduct*(1+rsratio)) entries

In case a delegator wishes to withdraw delegation(s), then the proportionate rewards are appropriated using the aforementioned scheme, which is implemented by the following function.

withdrawalAmount 
:: HashMap<Address, HashMap <Epoch, Product>> 
-> BondId 
->  [(Epoch, Delegation)] 
-> Token::amount

withdrawalAmount validatorProducts bondId unbonds = 
 sum [stake * endp/startp | (endEpoch, unbond) <- unbonds, 
                            let epochProducts = lookup (validator bondId)
                           validatorProducts, 
                            let startp = lookup (startEpoch unbond) 
                       epochProducts, 
                            let endp = lookup endEpoch epochProducts, 
                            let stake =  delegation unbond]
 

Commission

Commission is charged by a validator on the rewards coming from delegations. These are set as percentages by the validator, who may charge any commission they wish between 0-100%.

Let be the commission rate for a delegation to a validator at epoch . The expression for the product that was introduced earlier can be modified for a delegator in particular as

in order to calculate the new rewards given out to the delegator during withdrawal. Thus the commission charged per epoch is retained by the validator and remains untouched upon withdrawal by the delegator.

The commission rate is the same for all delegations to a validator in a given epoch , including for self-bonds. The validator can change the commission rate at any point, subject to a maximum rate of change per epoch, which is a constant specified when the validator is created and immutable once validator creation has been accepted.

While rewards are given out at the end of every epoch, voting power is only updated after the pipeline offset. According to the proof-of-stake system, at the current epoch e, the validator sets an only be updated for epoch e + pipeline_offset, and it should remain unchanged from epoch e to e + pipeline_offset - 1. Updating voting power in the current epoch would violate this rule.

Distribution to validators

A validator can earn a portion of the block rewards in three different ways:

  • Proposing the block
  • Providing a signature on the constructed block (voting)
  • Being a member of the consensus validator set

The reward mechanism calculates fractions of the total block reward that are given for the above-mentioned three behaviors, such that

where is the proposer reward fraction, is the reward fraction for the set of signers, and is the reward fraction for the whole active validator set.

The reward for proposing a block is dependent on the combined voting power of all validators whose signatures are included in the block. This is to incentivize the block proposer to maximize the inclusion of signatures, as blocks with more signatures are (JUSTIFY THIS POINT HERE).

The block proposer reward is parameterized as

where is the ratio of the combined stake of all block signers to the combined stake of all consensus validators:

The value of is bounded from below at 2/3, since a block requires this amount of signing stake to be verified. We currently enforce that the block proposer reward is a minimum of 1%.

The block signer reward for a validator is parameterized as

where is the stake of validator , is the combined stake of all signers, and is the combined stake of all consensus validators.

Finally, the remaining reward just for being in the consensus validator set is parameterized as

Thus, as an example, the total fraction of the block reward for the proposer (assuming they include their own signature in the block) would be:

The values of the parameters and are set in the proof-of-stake storage and can only change via governance. The values are chosen relative to each other such that a block proposer is always incentivized to include as much signing stake as possible. These values at genesis are currently:

These rewards must be determined for every single block, but the inflationary token rewards are only minted at the end of an epoch. Thus, the rewards products are only updated at the end of an epoch as well.

In order to maintain a record of the block rewards over the course of an epoch, a reward fraction accumulator is implemented as a Map<Address, Decimal> and held in the storage key #{PoS}/validator_set/consensus/rewards_accumulator. When finalizing each block, the accumulator value for each consensus validator is incremented with the fraction of that block's reward owed to the validator. At the end of the epoch when the rewards products are updated, the accumulator value is divided by the number of blocks in that epoch, which yields the fraction of the newly minted inflation tokens owed to the validator. The next entry of the rewards products for each validator can then be created. The map is then reset to be empty in preparation for the next epoch and consensus validator set.

TODO describe / figure out:

  • how leftover reward tokens from round-off / truncation are handled