Forcibly reduce credits_observed to the last epoch#13233
Forcibly reduce credits_observed to the last epoch#13233ryoqun wants to merge 3 commits intosolana-labs:masterfrom
Conversation
Codecov Report
@@ Coverage Diff @@
## master #13233 +/- ##
=========================================
+ Coverage 82.0% 82.2% +0.1%
=========================================
Files 378 378
Lines 90905 91258 +353
=========================================
+ Hits 74630 75073 +443
+ Misses 16275 16185 -90 |
Yep we want to fix this! The effect is small but we don't need a muddled story for how inflation gets turned on and having to explain why that first epoch after enabling inflation is special. |
|
Wow, test passed. |
| self.get_account(&delegation.voter_pubkey), | ||
| ) { | ||
| (Some(stake_account), Some(vote_account)) => { | ||
| (Some(mut stake_account), Some(vote_account)) => { |
There was a problem hiding this comment.
this is least intrusive way. but a hacky way at the same time.
I'll do more commenting
070712d to
24ecc57
Compare
|
This'll need a feature gate so it can be deployed out on testnet/devnet right? |
Yeah, it seems so. I originally thought that this shouldn't cause any calculation changes for cluster where inflation is already enabled. But it seems not. ;) I'm now working on this. |
We turned off inflation for devnet/testnet to test the inflation kill switch, and to make it easier to deploy this across all clusters. No gating required now I think! |
24ecc57 to
4065931
Compare
5e14228 to
9bf5c3e
Compare
| stake_context: Option<(Epoch, &StakeHistory)>, | ||
| inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>, | ||
| ) -> (u128, u64) { | ||
| // if there is no newer credits since observed, return no point |
There was a problem hiding this comment.
move comment along...
|
| if epoch != target_epoch { | ||
| return (0, 0); | ||
| } |
There was a problem hiding this comment.
write a test for this; this bug is later found after manual testing.
There was a problem hiding this comment.
well, if we don't exclude historical vote_state's epoch credits by doing like this early return, this way, we might accidentally reward stale delegations using its (last and stale) epoch_credits, which is well past to target_epoch.
9bf5c3e to
8c41fac
Compare
|
I happened back across this bug while investigating the viability of allowing fully-activated stake accounts to be merged. Would an alternate (and cleaner?) solution be to ensure that the stake account's I think this can be achieved by changing the early return here... solana/programs/stake/src/stake_state.rs Lines 598 to 601 in f25c969 ... to something like ... ... then everything resolves itself at the next epoch boundary. I say, "something like," because I haven't yet checked whether This solution has the side-effect that I believe we can safely merge two qualified fully activated stake accounts with just a few extra checks from the deactivated case. @ryoqun @CriesofCarrots @mvines WDYT? |
| let mut points = 0; | ||
| let mut new_credits_observed = self.credits_observed; | ||
|
|
||
| for (epoch, final_epoch_credits, initial_epoch_credits) in |
There was a problem hiding this comment.
Looking into this further... I believe this change can deny small stakes delegated to validators with small, but non-zero commissions ever getting paid out, as per
solana/programs/stake/src/stake_state.rs
Lines 611 to 616 in d9a07e7
There was a problem hiding this comment.
yeah! correct! I intentionally done this way now. My two cent is posted here: #13680 (comment)
@t-nelson , it looks to me like your suggestion will resolve the issue motivating this PR nicely. The only caveat, as we discussed in DM, is that we need to activate your fix at least one epoch before we activate inflation (to prevent the additional rewards that @ryoqun describes on the epoch that I will hold off activating stake-program-v2 on testnet so that we don't have to plumb an additional feature. |
Problem
The initial inflation reward could be unexpected if inflation has not been enabled since genesis. (i.e. Virtually all of live clusters)
Subsequent inflation rewards are correct, though.
Conceptually, inflation rewards should be a fair distribution of a stake-weighted contribution metrics (called points) across validators for some reasonable recent timespan.
The current implementation fulfils it for all inflation rewards but the first one.
The reason is that the initial inflation reward calculation is based on the full timespan since genesis.
Specifically, the initial inflation calculation uses all epochs since genesis as the timespan for points summation.
This is due to implantation remnant before periodic forced inflation rewards distribution (the
solana redeemage)Technically, this is caused because stake account's
credits_observed(key component of points calculation) aren't updated at all, until the very first inflation reward.After that, as
credits_observedis initialized, the inflation is calculated for each last epoch.This creates the following counter intuitive inflation rewards.
As an extreme example, we reward currently undelegated stake account. (But this happens only once at the first epoch after inflation is enabled)
Currently,
HDAhLXUZmFyvxT2gnGv36opsU6ZAXEEmcEzHMAghTBoTis undelegated:But, if we would enable the inflation now, the stake account receives
+0.000016081SOL. That's because initial inflation doesn't only look at the last epoch, but the entire blockchain history. So, this matches to the fact the stake account was actually delegating some SOLs (=~ ◎60) from epoch 58 to epoch 65:For comparison, similarly-sized stake account only receives
0.000000014SOL.In other words, staker will be rewarded proportional to the full delegated duration, not only the last epoch, with current implementation. So, this is a situation of act sooner and earn more.
Summary of Changes
Ideally, I think we should only consider the last epoch?
To realize that, this pr forcibly overwrites credits_observed before calculating inflation.
But this phenomenon is only one-time. and pico-inflation reward is very small. So, might not worth to fix this...
Context
Found via
ledger-tool capitalizationcheck.