Add SIMD-22: Multistake proposal (#22)

* Add multistake proposal

* Don't talk about slashing

* Address Trent's feedback

* Only have two Delegations total

* Clarify upgrade, make update permissionful, no one cares who I am

* Clarify "small stake" and liquid amounts left unstaked
This commit is contained in:
Jon Cinque 2023-02-03 23:32:08 +01:00 committed by GitHub
parent 6b0c334e43
commit 5b00382f24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 184 additions and 0 deletions

View File

@ -0,0 +1,184 @@
---
simd: '0022'
title: Multi Delegation Stake Account
authors:
- Jon Cinque (Solana Labs)
category: Standard
type: Core
status: Draft
created: 2023-01-20
feature: (fill in with feature tracking issues once accepted)
---
## Summary
A new "multi stake" account that allows for multiple movements of stake, useful
for simpler stake movement through more activations, deactivations, or redelegation.
## New Terminology
* "multi stake": a stake account with multiple `Delegation` instances
* "small stake": a stake whose delegation is less than current minimum stake delegation amount
## Motivation
Stake operations are cumbersome or impossible for many ordinary uses. For example,
delegating more lamports in an existing stake account requires creating a new
account, delegating, waiting, merging, then withdrawing the rent-exempt lamports.
The current stake `redelegate` instruction requires using a new stake account and
eventually cleaning up the old one, which can be tricky to use.
As a minimum stake delegation amount is applied to the network, and potentially
increased over time, these operations will become more complicated, since all
delegations must clear that threshold. Small SOL amounts will be left liquid rather
than delegated to a validator.
Additionally, stake pools always carry some risk or capital inefficiency for the
stake pool operator. Either there's a requirement to leave liquid SOL available
for small stakers to exit, or small stakes cannot enter because there's not enough
to cover the minimum delegation amount.
Small stakes need to be delegated, while not causing problems in the validator's
stakes cache.
For more background, here's an earlier proposal meant for only for redelegation:
https://github.com/solana-labs/solana/pull/24762
## Alternatives Considered
The easiest solution is to change the validator's stakes cache, since that only
impacts the specific implementation in the Solana Labs validator.
In the perfect situation, small stakes are properly delegated, but don't receive
rewards. Meaning:
* they don't take up space in the stakes cache
* they take the correct amount of epoch boundaries to activate / deactivate
* they are included in a validator's voting power
There are a few ways of solving this at the stakes cache level, and they all have
serious issues.
* Include small stakes in the cache, but don't pay out rewards
This is the simplest solution, which essentially imposes a "minimum reward earning
amount". So you can delegate small stakes, but they won't earn rewards.
Unfortunately, small stakes can still bloat the stakes cache from a memory level.
* Don't include small stakes in the cache, but track them in transactions
In this solution, on every transaction that manipulates a small stake delegation, it
includes its delegation amount in the validator's voting power, but doesn't
add its pubkey and `Delegation` to the cache.
The runtime tracks the pre and post state of every account, and for a successful
transaction, subtracts the old small stake delegation amount, and adds the new small
stake delegation amount.
During rewards payout, small stakes are completely omitted, since they are not
present in the cache.
This is incredibly brittle, however, and introduces more overhead in the runtime.
The bank must hold onto pre-states for all transactions to debit the stakes cache. And,
if called from the outside, all `store_accounts` functions on the bank could easily
invalidate the stakes cache.
For example, if you store a small stake directly, what does the cache do? What
was the pre-state of that small stake? There's no way to know.
## Detailed Design
Rather than hacking the validator, let's change the stake program.
With a new "multi delegation stake" account, carrying *two* `Delegation` instances,
the stake program becomes much more flexible.
Here are some example uses:
### Add or remove small stake
To add a small stake to your account, transfer the lamports, call `delegate` once
more, and now your stake account has two delegations: the initial one, and the
newly activating one.
Once the new `Delegation` is active, an `update` instruction follows
the existing `merge` logic to consolidate the two `Delegation`s into one.
The same roughly applies for removing a small stake. You `deactivate` a portion
of it, which adds another delegation to your stake account: the initial one, and
the newly deactivating one.
### Redelegate
With another `Delegation` instance, you can redelegate from one validator to another
within the same account.
It works the same as the existing "redelegate" instruction, except the lamports
all stay in the same account, and the second `Delegation` covers the redelegation.
The `update` instruction clears out the first one once it's inactive.
### Upgrade
The stake program exposes a new instruction to upgrade a stake account to a multi-stake
account. It performs a realloc to the new size of the stake account, and
updates the rent-exempt reserve field in the stake account `Meta`.
It must be signed by the current staker or withdrawer, and takes an optional
payer to fund the additional rent-exempt reserve requirement.
### Runtime
The main difference to the runtime is processing up to two `Delegation`s
per account.
There's minor impact on the stakes cache, which gets a little bigger for the
additional `Delegation` instances.
There's minor impact on rewards payout, since the time spent calculating the rewards
amount is negligible compared to the time spent storing the accounts. The number
of account storages stays the same.
## Impact
Validators likely see a small increase in memory usage from additional `Delegation`
entries in the stakes cache, and perhaps a tiny increase in rewards payout time,
but nothing substantial.
Dapp developers have to deal with a new stake account type. This may break programs
that try to deserialize any stake account.
The network benefits as a whole:
* stake accounts are more flexible for adding or removing stake, without splits and merges
* stakers can easily redelegate without missing out on rewards
## Security Considerations
It's risky to change the runtime, especially the stake program and rewards payout.
Thankfully, since this all operates on the level of the `Delegation`, as long
as we pass all the instances as needed, none of the most sensitive parts need
to change.
## Backwards Compatibility *(Optional)*
While existing programs that create and use their own stake accounts are not impacted,
those that accept any stake account will crash on multi-stakes. There's no way
around that, since the proposal entails a new stake account variant.
## Appendix: Single validator stake pool
A single-validator stake pool program can manage small stakes with 100% efficiency
using "multi stakes".
With a total of *two* `Delegation` instances, the program can add or remove small
stakes. One instance covers the main amount, and the other covers new activations
or deactivations.
For small stake deposit, you simply transfer the lamports and activate the new amount.
If a user wishes to withdraw, the program first withdraws from the activating amount.
If there is no more activating amount available, then the pool deactivates from
the main delegation and provides a ticket to the user, used to claim their lamports after deactivation.