-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Refactor and use MINIMUM_STAKE_DELEGATION constant #22663
Refactor and use MINIMUM_STAKE_DELEGATION constant #22663
Conversation
Codecov Report
@@ Coverage Diff @@
## master #22663 +/- ##
========================================
Coverage 81.3% 81.4%
========================================
Files 572 573 +1
Lines 155876 156739 +863
========================================
+ Hits 126815 127638 +823
- Misses 29061 29101 +40 |
... or should I name this minimum stake delegation? (Or minimum stakable balance?) |
We want to capture a "minimum stake delegation" and that constant needs to be reflected when delegating stake and splitting stake. |
@jstarry Constant has been renamed to |
Cool but I don't think it makes sense to use this constant the way you're using it for withdraw. Why not use it for delegations and splitting stake like I suggested? |
Ok, will do. I was trying to keep this PR as small as possible, then making further changes in subsequent PRs. I'll pull those in here. |
bae5a1a
to
a5063fe
Compare
@joncinque @jstarry This PR is now ready for a re-review! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! This is really close, just a few small comments,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me! perhaps @CriesofCarrots or @ryoqun could also take a look since this code is pretty crucial, and I believe they did the initial work around here
Pull request has been modified.
0fd962b
to
e66ae1e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm requesting a couple comments, but the logic looks correct to my eye. I do think we should get @ryoqun 's eyes+approval also 🙏
Pull request has been modified.
Can you update the title and description? There are clearly logical changes here and I don't want to assess them without understanding the motivation |
Ok, I've updated the title and description. In theory there actually are no changes to the current behavior. This PR really is future-proofing and future-prepping changes to the minimum stake delegation. I added this bit to the description:
Does that feel sufficient? If not I'll happily give it another go to explain it more/more clearly. |
@ryoqun Would you be able to take a look at this PR too? Thanks in advance! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, just one comment which might be important for enforcing MINIMUM_STAKE_DELEGATION
on active source stakes
b26cf9d
to
1a2384d
Compare
1a2384d
to
74ba897
Compare
@ryoqun @joncinque Tests have been added and merged, and this PR rebased. Good for another round of reviews. Thanks in advance! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really sorry that this keeps getting dragged out, but it's really really close!
programs/stake/src/stake_state.rs
Outdated
destination_rent_exempt_reserve.saturating_add(MINIMUM_STAKE_DELEGATION); | ||
let destination_balance_deficit = | ||
destination_minimum_balance.saturating_sub(destination_account.lamports()?); | ||
let minimum_split_amount = std::cmp::max(MINIMUM_STAKE_DELEGATION, destination_balance_deficit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't think I made the contra to this part clear. There could be a different strange behavior with this, where anytime I split an Initialized
stake into an account that already has destination_minimum_balance
, I can't split anything less than MINIMUM_STAKE_DELEGATION
, which is fine for now, but once the number is big, it means I'll have to do a withdraw
+ transfer
, which is probably fine, but something to keep in mind.
The best of all behaviors would be to take the max
only if source_account
is a Stake
, which can be expressed through an additional is_stake
flag or something. Does that make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, OK. I glossed over that in your other comment, sorry! I've just pushed another commit that hopefully fixes this.
I put the changes inside the match within split()
, since I wanted to try and keep validate_split_amount()
to be state-agnostic. I also did not add a check if splitting 100%, since I'm making the assumption that the source account meets the minimum requirements.
(But note, what happens if the MINIMUM_STAKE_DELEGATION is raised and there's an existing stake account that tries to split 100%? Should that succeed? Should that fail? I'm not sure. Luckily that doesn't need to be answered immediately for this PR.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(But note, then what happens if the MINIMUM_STAKE_DELEGATION is raised and there's an existing stake account that tries to split 100%. Should that succeed? Should that fail? I'm not sure. Luckily that doesn't need to be answered immediately for this PR.)
Hehe indeed not for this PR, but my thinking was that only merge / deactivate / withdraw would be supported for existing stakes underneath MINIMUM_STAKE_DELEGATION
. You could even talk me out of "merge"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, thanks for your patience!
@ryoqun I will wait for your review before merging, unless you say otherwise. (No rush, just wanted to make sure I was explicit!) |
(sorry for dragging this pr so long for extended review cycles from me...) |
// If the source account is already staked, the destination will end up staked as well. Verify | ||
// the destination account's delegation amount is at least MINIMUM_STAKE_DELEGATION. | ||
// | ||
// The *delegation* requirements are different than the *balance* requirements. If the | ||
// destination account is prefunded with a balance of `rent exempt reserve + minimum stake | ||
// delegation - 1`, the minimum split amount to satisfy the *balance* requirements is 1 | ||
// lamport. And since *only* the split amount is immediately staked in the destination | ||
// account, the split amount must be at least the minimum stake delegation. So if the minimum | ||
// stake delegation was 10 lamports, then a split amount of 1 lamport would not meet the | ||
// *delegation* requirements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i love this crisp and lucid explanation. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@brooksprumo lgtm
thanks for your patience with my perfectionism and stubbornness. i think this pr is in flawless state now. excellent work.
by the way, what do you think about shipping this? I'd suggest 2 more things to solidify our confidence of those changes:
- run locally built solana-validator for several epochs against mainnet-beta/testnet/devnet (should see no bank hash mismatch then)
- request neodyme and others to informally review this (now that this is mergeable anytime, it's applicable to bounties here https://github.com/solana-labs/solana/blob/master/SECURITY.md; no need to risk our mainnet-beta; see Credits auto rewind on vote recreation #22546 (comment) for reference and i ping you on discord as well)
(I'm being paranoia here because this is editing super critical code.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, just two very picky suggestions, which are more of a personal opinion so feel free to ignore if you don't agree :)
account: &KeyedAccount, | ||
meta: &Meta, | ||
) -> Result<ValidatedDelegatedInfo, InstructionError> { | ||
let stake_amount = account.lamports()?.saturating_sub(meta.rent_exempt_reserve); // can't stake the rent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function is called validate_delegated_amount
but uses saturating_sub
which won't throw an error if the balance is less than rent exempt reserve. Is it possible to use checked_sub
instead of the saturating one and throw an error when the sub fails, or are there already stake accounts on chain which would fail in that case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current behavior is intentional. (1) In the case where an account's balance is below the rent exempt minimum, then it has zero delegate-able lamports. (2) Adding new errors and/or changing what conditions return errors will change the program's behavior, which would break consensus without a feature-gate.
(In an initial iteration of this PR I had checks within this function to error in these cases, but I learned that wouldn't be a good idea to add—for the above reasons.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, makes sense
} else { | ||
// all clear! | ||
// nothing to do here | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is very picky but you could write this as:
let source_enough_lamports = source_remaining_lamports >= source_minimum_balance; || source_remaining_balance == 0
if !source_enough_lamports {
return Err(InstructionError::InsufficientFunds);
}
which to me more directly expresses the condition stated in the comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted. I think I'm going to leave the code as-is so that I don't have to run (and babysit) CI again. Let me know if that's not OK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's totally okay
Sounds like a good idea to me; I'll get on this. To be clear, I should run validators on all three networks?
Also sounds like a good idea! |
I'd recommend all of them. i know that sounds tedious but the tediousness is 1000x better than the mess of bug at mainent-beta, imo. also, you can spawn 3 gcp instances at the same time and just run your binary there and just wait. I assume this isn't too much of hassle. Previously, I found a bug of stake program only exhibited at testnet's live dataset. (devnet and testnet usually have very odd data; nice exercise for your hot-pressed code). Also this pr's nature is negative feature activation. i mean, its somewhat large code changes must not deviate from the current behavior in every corner of cases. so, it needs dacent track record before reaching to mainnet-beta regardless of urgency (assuming you think this worth backport for upcoming actual change). on the other hand, merging and v1.10-backporting can comes first to avoid merge conflicts or if it's convenient for you (only pend v1.9-backporting), so that the above general principle is honored. :)
👍 seems we got it already: ref: #22663 (review) (@bennofs thx!) |
Ok, I've let this PR run on mainnet-beta, testnet, and devnet for multiple epochs. I'm going to merge this in!
|
congrats! :) it's really my joy to see this pr to be merged finally. :) |
Problem
The minimum stake delegation constant is not used outside of tests.
Summary of Changes
initialize()
,delegate()
,split()
, andwithdraw()
Notes