Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Locked stake, fixed epochs & fair validating rewards #1135

Open
bdmason opened this issue Jan 8, 2025 · 2 comments
Open

Locked stake, fixed epochs & fair validating rewards #1135

bdmason opened this issue Jan 8, 2025 · 2 comments

Comments

@bdmason
Copy link

bdmason commented Jan 8, 2025

Is your feature request related to a problem? Please describe.

Validator rewards don't reflect validator performance

In the current runtime the movement of stake to and from a validator (or one of their children) inflates the rewards. In the dTao runtime the movement of stake has the opposite effect and deflates the rewards.

Current Runtime

When a nominator adds stake their stake it is immediately available to the validator to earn rewards in subnet epochs (which are approx hourly), however it is considered non-viable for the nominator until the next hotkey epoch (daily). So for up to a whole day the stake of a new nominator will be earning rewards for a validator that will be shared between everyboth but themselves at the next hotkey drain. This causes nom/kt to be spiked as has been explained before when there was a very real case of a nom/kt of 3091 TAO for a small validator one day when a whale moved in.

Removing stake has the same effect too.

dTao Runtime

As hotkey epochs have been removed, the concept of non viable stake has gone with it. Staking rate limits have also been removed, perhaps because slippage is now considered a big enough deterrent to frequent stake movements.

The effect of completely free movement of stake and it being viable for rewards at each subnet epoch is the opposite of the current runtime. Take this trivialised example:

  1. A subnet epoch starts and a validator has a total of 10 Alpha staked in the subnet.
  2. The epoch runs and the validator earns rewards based on a combination its performance & the 10 Alpha staked.
  3. On the penultimate block of the subnet epoch a new staker adds 10 Alpha to the subnet.
  4. The subnet epoch ends and the rewards that were based on the original 10 Alpha stake are now shared amongst the validator based on the final stake value of 20 Alpha.

In this example the new staker has suppressed the rewards of the validator by 50% for the subnet epoch. This is the opposite of the current runtime where the rewards are inflated by the new stake.

It may be the case that slippage makes this economically unviable, but it offers a way for people to intentionally supress a validators rewards on chain to make them less appealing to stakers. Much like the current runtime offers a way to boost a validators rewards by sacrificing your own stake.

Describe the solution you'd like

Solution

In the past having a lock up period to ensure fair rewards by fixing stake to a full epoch was dismissed as the freedom of movement was considered more important. But now with dTao the maximum epoch is roughly an hour, very little freedom of movement needs to be sacrificed to ensure fair rewards.

When add_stake and remove_stake are called add the stake into a pending state until the subnet epoch runs.

Describe alternatives you've considered

No response

Additional context

At Taostats we know our current nom/kt is 100% accurate for each hotkey epoch because we simplified our way of calculating it. We simply take a nominators viable stake the block before the epoch end and on the block of the epoch end and use the difference as the profit input in the nom/kt calculation.

Since using this method we have seen some strage things on chain, most notably the day where a validator had 3091 TAO as their nom/kt (more than quadrupling every nominators stake balance on that day). Every time we investigated these things we always found the npm/kt figure was correct.

@unconst
Copy link
Contributor

unconst commented Jan 13, 2025

/ 6. Swap the stake into alpha on the subnet and increase counters.
        // Emit the staking event.
        let alpha_staked: I96F32 = I96F32::from_num( Self::stake_into_subnet(&hotkey, &coldkey, netuid, tao_staked) );

        // 7. Pay staking fee as proportion of staking fee.
        let last_alpha_dividend: I96F32 = I96F32::from_num( AlphaDividendsPerSubnet::<T>::get( netuid, hotkey.clone() ) );
        let total_alpha_on_hotkey: I96F32 = I96F32::from_num( TotalHotkeyAlpha::<T>::get( hotkey.clone(), netuid ) );
        let divs_per_alpha: I96F32 = last_alpha_dividend.checked_div( total_alpha_on_hotkey ).unwrap_or(I96F32::from_num(0.0));
        let divs_per_alpha_staked: I96F32 = alpha_staked.saturating_mul( divs_per_alpha );
        Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid, divs_per_alpha_staked.to_num::<u64>() );
        Self::increase_stake_for_hotkey_on_subnet( &hotkey, netuid, divs_per_alpha_staked.to_num::<u64>() );

This is an alternative design which makes the staking fee equivalent to the amount of dividends this staked amount would receive during the next epoch.

It computes this amount, subtracts it from the staking account and adds it to the total.

@bdmason
Copy link
Author

bdmason commented Jan 13, 2025

That design doesn't ensure the emission distribution is exactly proportional to the stake and performance of the validator. It has 2 problems:

  1. It takes the dividends from the previous epoch which will never be exactly the same as those in the current epoch unless there is zero movement of tap/alpha stake, and as this happens on a staking action we know there definitely has been stake movement.
  2. It doesn't take into account how far into an epoch the stake event is, so if it's at block 1 of the epoch the fee will be unfair to the staker as the new stake has been at work for an entire epoch. This allows for validators to inflate their rewards by using their TAO to stake into a subnet on block 1 of an epoch, pay a massive fee, then unstake out of a subnet at the last block of an epoch, distributing the rewards they would have earned to the rest of the nominator, effectively doubling the problem.

Things could get very complicated and exploitable quite quickly. I understand the idea of free stake movement and how it's a positive thing, but it is currently being abused on chain to boost returns (in a technically honest way as those returns are real, but they are not based on the actual performance of the validators). Now we don't need to worry about a day long hotkey epoch, locking up for a subnet epoch keeps movement relatively free, and the people who want to stake & unstake with frequency higher than subnet epochs are very likely to be trying to grift rather than contribute to the network.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants