📈Rewards

This pages discusses the cases of rewarding for all providers and the calculations of the reward

Miner and Sharder rewards

Miner, sharders and their delegates receive rewards each round. There is a lottery every round where a subset of the miners, sharders and their delegates are chosen to receive the rewards.

There are two types of rewards paid to miners and sharders.

  1. Block rewards. This is a fixed amount paid out every round. The amount is found in the smart-contract config minersc.block_reward field.

  2. Income. This is a fees paid for transactions run this round.

The rewards are totalled and split between miners and sharders in the ratio minersc.share_ratio:1-minersc.share_ratio. Usually, we want to choose share_ratio so the payments to miners equal the payments to sharders.

total reward = block_reward + total fee payments

Miners

Every round, one of the active miners is chosen to receive all the miner rewards for that round. The miner receives a fraction equal to the chosen miner's service charge. The remaining reward is split up between the miner's delegates.

miner service charge = total_reward * share_ratio * miner's_service_charge

Miner Delegates

When paying rewards to delegates, we pay only a subset of the delegates. For miners, the number of delegates we reward is given by the setting minersc.num_miner_delegates_rewarded. We choose at ransom minersc.num_miner_delegates_rewarded delegates to receive rewards or all delegates if the number is less than minersc.num_miner_delegates_rewarded. We then split the reward up between the selected delegates in ratio to their balance.

paid to a miner's delegate pools = total_reward * share_ratio * (1-miner's_service_charge)
individual delegate reward = paid to delegate pools * delegate's balance / total balances of selected delegates

Sharders

Every round the 0chain selects minersc.num_sharders_rewarded active sharders at random to receive that round's reward. The sharder share of the reward is split evenly amongst the selected sharders.

Each sharder gets a fraction of that sharder's reward, determined by the shader's service charge.

sharder service charge = total_reward * (1-share_ratio) * sharder's_service_charge / num_sharder_delegates_rewarded

Sharders Delegates

Sharder delegates are rewarded similarly to miners. For each sharder, we select minersc.num_sharder_delegates_rewarded delegates to receive a reward, or all delegates if less.

paid to a sharder's delegates = total_reward * (1-share_ratio) * (1-miner's_service_charge) / num_sharder_delegates_rewarded
individual delegate reward = paid to delegate pools * delegate's balance / total balances of selected delegates

Releated config values

All these values are initially set from sc.yaml, the smart_contracts.minersc key

block_reward:
share_ratio: 0.5
num_miner_delegates_rewarded: 10
num_sharders_rewarded: 1
num_sharder_delegates_rewarded: 5

We also use the miner and sharder service charge in the calculations. A typical value for this would be 0.1.

Blobber And Validator rewards

Block rewards

Every round generator adds blobber_block_rewards transaction to the block, the only input parameter is a round for what calculation is triggered ?can we take round from the block itself?.

Reward amount calculation

Amount to be rewarded is calculated by formula:

	changeBalance := 1 - conf.block_reward_change_ratio
	changePeriods := currentRound / conf.block_reward_change_ratio

	factor := math.Pow(changeBalance, float64(changePeriods)) * conf.blobber_weight
	return currency.MultFloat64(br, factor)

current config values

      block_reward:
      block_reward: 1
      block_reward_change_period: 10000
      block_reward_change_ratio: 0.1

Active blobbers

Active blobbers are chosen from partitions we created earlier. BlobberRewardNode is added to partition every time challenge is passed.For every trigger_period: 30 new partition is created. ?how do we ensure that blobber is unique?

type BlobberRewardNode struct {
	ID                string        `json:"id"` //blobberID
	SuccessChallenges int           `json:"success_challenges"`
	WritePrice        currency.Coin `json:"write_price"`
	ReadPrice         currency.Coin `json:"read_price"`
	TotalData         float64       `json:"total_data"`
	DataRead          float64       `json:"data_read"`
}

Random seed

Random seed is calculated using formula

	hashString := encryption.Hash(balances.GetTransaction().Hash + balances.GetBlock().PrevHash)
	var randomSeed int64
	randomSeed, err = strconv.ParseInt(hashString[0:15], 16, 64)
	if err != nil {
		return common.NewError("blobber_block_rewards_failed",
			"error in creating seed"+err.Error())
	}
	r := rand.New(rand.NewSource(randomSeed))

Reward distribution

For every blobber in partition there weights are calculated using formula from white paper (Alfa, Gamma, Zetta). Reward amount is distributed then among all the blobbers in the partition according to weights calculated before. Every blobber distributes its fraction of rewards according to standard algorithm among all the delegates ?provide link to algo?

Challenge rewards

Every time challenge is passed blobber and validator are rewarded.

Challenge pool

Challenge pool stores token received from clients for it's writes and keeps them until allocation challenge is completed, tokens can be moved to the blobber in case of success, to the validator as a reward or in case of slashing, and to the client in case of failed challenge (slashing) or in case of data delete.

Every time write marker is committed a portion of tokens from allocation write pool is transferred to the challenge pool. The size written to allocation is rounded to the chunk size first (64kB as for now). Then rest duration in time units is calculated as an amount of timeunits to the allocation expiry.

        rdtu = (allocation.Expiration - WriteMarker.Timestamp) / timeunit

Then the amount of tokens to be transferred to the pool is computed as

        amount = size * Terms.WritePrice * rdtu

This amount is transferred to the challenge pool, also this amount is added to ChallengePoolIntegralValue

Challenge Pool Integral Value

To keep track of challenge pool fraction related to a particular blobber-allocation pair integral value was introduce. It behaves exactly the same way and should be always in sync with corresponding challenge pool. It means, that the summary of integral values for every blobber for a current allocation is exactly the challenge pool balance for any moment in time.

Partition management

Blobber is added to the partition for block rewards calculation on every first challenge passed this rewards period. All the fields in BlobberRewardNode except ChallengesPassed are fixed for that moment, only ChallengesPassed will be updated next time challenge is passed.

Reward amount calculation

Reward is calculated based on time unit values. All the available for reward values are stored in blobber allocation itself in ChallengePoolIntegralValue field. It is reduced every time reward is calculated

            rdtu = (allocation.Expiration - PreviousCompletedChallenge.Created) / timeunit
            dtu  = (CurrentCompletedChallenge.Created - PreviousCompletedChallenge.Created) / timeunit
	    reward = (dtu / rdtu) * float64(d.ChallengePoolIntegralValue)
	    d.ChallengePoolIntegralValue -= reward

If intervals dtu are even, then rewards will be split evenly between the intervals for constant ChallengePoolIntegralValue (it will be spent evenly in the intervals).

Reward then is split in two part, blobberReward and validatorReward using provided in config ratio.

        validatorsReward = reward * config.validator_reward
        blobberReward = reward * (1 - config.validator_reward)

Reward value is also adjusted to the success rate of a blobber. If rewards are adjusted, difference is taken from challenge pool and burnt.

        partial = float64(challenge.SuccessfulTickets) / float64(challenge.TotalValidators / 2)
        blobberReward = blobberReward * partial

Reward distribution

Reward amount is distributed among all the blobbers evenly. Every blobber distributes its fraction of rewards according to standard algorithm among all the delegates.

  • If provider doesn't have any delegates, then all the rewards go to provider's stake pool

  • If service charge is set, then provider will be guaranteed to receive service charge

  • Then value left is split among all the delegates of a provider proportionally with their weight

		stakePool.Reward = stakePool.Reward + value
		sp.Reward = stakePool.Reward + serviceCharge
		valueLeft = value - serviceCharge
	        for id, pool := range sp.Pools {
		    ratio := pool.Balance / totalStake
		    reward := valueLeft * ratio
                    delegate.Reward = delegate.Reward, reward
                    valueLeft = valueLeft - reward
                }

Validator rewards

  • Validators get rewards when a challenge response is computed, whatever the status of the challenge is.

  • All the validators of the challenge get an equal share of the total share of the validators from the challenge rewards.

  • This reward is calculated as follows:

     validatorsReward = challenge_reward * config.validator_reward

where config.validator_reward is a constant value set in the configuration file sc.yaml. Refer to Blobber Rewards section for more details on the challenge reward.

Authorizer Rewards [TODO]

Last updated