iotaledger / iota

Apache License 2.0
7 stars 4 forks source link

[gas-price-modulation-tf]: Burn slashed rewards #3123

Open vekkiokonio opened 1 month ago

vekkiokonio commented 1 month ago

In this phase, the grading and reporting of validators remain unchanged:

  1. Validators report non-behaving peers
  2. Those reports are aggregated, and the set of slashed validators is computed using a fixed maximum threshold for the stake of reporters

Instead of adjusting rewards so that slashed rewards are assigned to non-slashed validators, we remove this adjustment, entirely burning the slashed rewards, which would go to the protocol instead of going to the non-slashed validator set. Even though this might seem to be a simplistic change, it has a significant impact on the incentivization properties of the protocol. Now, validators don’t have any incentive to attack or mis-report their peers, which is an undesirable property present in both SUI and IOTA Rebased.

vekkiokonio commented 1 month ago

In order to decouple rewards for slashed and non-slashed validators, the following modifications in the codebase are required:

  1. The function _compute_adjusted_rewarddistribution should subtract adjustments for the slashed nodes, but remove the adjustment addition to non-slashed ones.
  2. The _total_staking_rewardadjustment should be burned, since it won’t be distributed to any validator.

In the current implementation, when _advanceepoch is called, the following steps (among others) will be performed in the presented order:

  1. Mint or burn IOTA tokens depending on whether the validator target reward is greater or smaller than the computation reward (_match_computation_reward_to_targetreward);
  2. Distribute the reward to the validators (_validators.advanceepoch);
  3. Burn any leftover rewards (_iota_treasury_cap.burnbalance).

Thus, the current implementation will already burn total_staking_reward_adjustment, without the need for any other modifications. The function compute_adjusted_reward_distribution will need a simple modification, as follows:

fun compute_adjusted_reward_distribution(
        validators: &vector<Validator>,
        total_voting_power: u64,
        unadjusted_staking_reward_amounts: vector<u64>,
        individual_staking_reward_adjustments: VecMap<u64, u64>,
    ): vector<u64> {
        let mut adjusted_staking_reward_amounts = vector[];
        let length = validators.length();
        let mut i = 0;
        while (i < length) {
            let validator = &validators[i];
            // Integer divisions will truncate the results. Because of this, we expect that at the end
            // there will be some reward remaining in `total_reward`.
            // Use u128 to avoid multiplication overflow.
            // Compute adjusted staking reward.
            let unadjusted_staking_reward_amount = unadjusted_staking_reward_amounts[i];
            let adjusted_staking_reward_amount =
                // If the validator is one of the slashed ones, then subtract the adjustment.
                if (individual_staking_reward_adjustments.contains(&i)) {
                    let adjustment = individual_staking_reward_adjustments[&i];
                    unadjusted_staking_reward_amount - adjustment
                } 
                else {
                    // The unadjusted staking reward amount is assigned to the unslashed validators
                    unadjusted_staking_reward_amount
                };
            adjusted_staking_reward_amounts.push_back(adjusted_staking_reward_amount);
            i = i + 1;
        };
        adjusted_staking_reward_amounts
    }

Note that in the code above, the signature of the function was modified, since the function does not use _total_staking_rewardadjustment and _total_unslashed_validator_votingpower anymore.