|
| 1 | +--- |
| 2 | +simd: '0123' |
| 3 | +title: Block Revenue Sharing |
| 4 | +authors: Justin Starry (Anza) |
| 5 | +category: Standard |
| 6 | +type: Core |
| 7 | +status: Review |
| 8 | +created: 2024-03-10 |
| 9 | +feature: (fill in with feature tracking issues once accepted) |
| 10 | +--- |
| 11 | + |
| 12 | +## Summary |
| 13 | + |
| 14 | +A new mechanism is proposed to allow validators to share part of their block |
| 15 | +revenue with their delegators. Commission rates from validator vote accounts |
| 16 | +will be used by the protocol to calculate post-commission rewards that will be |
| 17 | +automatically distributed to delegated stake accounts after an epoch is |
| 18 | +completed. |
| 19 | + |
| 20 | +## Motivation |
| 21 | + |
| 22 | +Delegated stake directly increases the number of blocks that a validator is |
| 23 | +allocated in an epoch leader schedule but the core protocol doesn't support |
| 24 | +diverting any of that extra revenue to stake delegators. |
| 25 | + |
| 26 | +## Dependencies |
| 27 | + |
| 28 | +This proposal depends on the following previously accepted proposals: |
| 29 | + |
| 30 | +- **[SIMD-0180]: Use Vote Account Address To Key Leader Schedule** |
| 31 | + |
| 32 | + Necessary for looking up a block producer's vote account |
| 33 | + |
| 34 | +- **[SIMD-0185]: Vote Account v4** |
| 35 | + |
| 36 | + Introduces version 4 of the vote account state, which adds new fields |
| 37 | + for block revenue commission and pending delegation rewards |
| 38 | + |
| 39 | +- **[SIMD-0232]: Custom Commission Collector Account** |
| 40 | + |
| 41 | + Necessary for looking up a block producer's commission collector account |
| 42 | + |
| 43 | +- **[SIMD-0291]: Commssion Rate in Basis Points** |
| 44 | + |
| 45 | + Introduces a new instruction type for setting commission rates in basis |
| 46 | + points |
| 47 | + |
| 48 | +[SIMD-0180]: https://github.com/solana-foundation/solana-improvement-documents/pull/180 |
| 49 | +[SIMD-0185]: https://github.com/solana-foundation/solana-improvement-documents/pull/185 |
| 50 | +[SIMD-0232]: https://github.com/solana-foundation/solana-improvement-documents/pull/232 |
| 51 | +[SIMD-0291]: https://github.com/solana-foundation/solana-improvement-documents/pull/291 |
| 52 | + |
| 53 | +## Alternatives Considered |
| 54 | + |
| 55 | +### Distribute Rewards as Activated Stake |
| 56 | + |
| 57 | +The runtime could ensure that any distributed stake rewards get activated as |
| 58 | +well but it would require extra complexity in the protocol to support that |
| 59 | +feature. Instead, stakers will receive inactive SOL in their stake accounts that |
| 60 | +they will have to manage themselves. [SIMD-0022] aims to make this experience |
| 61 | +better for stakers by allowing stake accounts to separately delegate any |
| 62 | +unstaked balance in their accounts. |
| 63 | + |
| 64 | +[SIMD-0022]: https://github.com/solana-foundation/solana-improvement-documents/pull/22 |
| 65 | + |
| 66 | +### Out of protocol reward distribution |
| 67 | + |
| 68 | +Due to the lack of core protocol support for distributing block revenue to |
| 69 | +stakers, validators have developed their own solutions which are not enforced by |
| 70 | +the core protocol. For example, the Cogent validator diverts part of its fee |
| 71 | +revenue to NFT holders. But it's up the NFT holders to audit and hold Cogent |
| 72 | +accountable to a specific commission rate. |
| 73 | + |
| 74 | +Another alternative is Jito's mechanism for block "tips" (not fees, but the idea |
| 75 | +is similar). Jito's validator implementation includes a tip distribution program |
| 76 | +which it instructs validator operators to divert all of their tips to but cannot |
| 77 | +enforce perfect compliance. It's up to stakers and the Jito team to audit |
| 78 | +compliance by validator operators. This mechanism requires trusting a |
| 79 | +third-party (in this case Jito) to calculate reward distribution in an accurate |
| 80 | +and fair manner. It also relies on using a merkle tree to distribute fees to all |
| 81 | +stake accounts and the distributed fees are not automatically staked in |
| 82 | +recipient stake accounts. |
| 83 | + |
| 84 | +## New Terminology |
| 85 | + |
| 86 | +NA |
| 87 | + |
| 88 | +## Detailed Design |
| 89 | + |
| 90 | +### Runtime: Block Revenue Collection |
| 91 | + |
| 92 | +After all transactions are processed in a block for a given leader, rather than |
| 93 | +collecting all block revenue into the validator identity account, the protocol |
| 94 | +will look up the block producer's vote account as described in [SIMD-0180]. Then |
| 95 | +it MUST check if the validator's vote account has specified a block revenue |
| 96 | +commission rate and collector addresses in the new vote account version |
| 97 | +described in [SIMD-0185]. As described in [SIMD-0232], the latest block revenue |
| 98 | +commission rate and collector address MUST be loaded from the vote account state |
| 99 | +at the beginning of the previous epoch. This is the same vote account state used |
| 100 | +to build the leader schedule for the current epoch. |
| 101 | + |
| 102 | +If the block revenue commission rate and collector account aren't set (e.g., the |
| 103 | +vote account state version has not been updated to v4 yet), all revenue will be |
| 104 | +collected into the validator's identity account as before. If the block revenue |
| 105 | +commission rate and collector account *are* specified, the rewards MUST be |
| 106 | +distributed according to the commission and delegator rewards collection |
| 107 | +sections below. |
| 108 | + |
| 109 | +#### Commission Collection |
| 110 | + |
| 111 | +The commission amount MUST be calculated by first multiplying the amount of |
| 112 | +revenue by the lesser of the vote account's block revenue commission rate or the |
| 113 | +maximum of `10,000` basis points. Then use integer division to divide by |
| 114 | +`10,000` and discard the remainder. If the commission amount is non-zero, the |
| 115 | +block revenue commission collector account MUST be loaded and checked for the |
| 116 | +following conditions: |
| 117 | + |
| 118 | +1. account is system program owned AND |
| 119 | +2. account is rent-exempt after depositing the commission. |
| 120 | + |
| 121 | +If the conditions are met, the commission amount MUST be deposited into the |
| 122 | +block revenue commission collector account. If either of these conditions is |
| 123 | +violated, the commission amount MUST be burned. |
| 124 | + |
| 125 | +#### Delegator Rewards Collection |
| 126 | + |
| 127 | +The delegator rewards amount MUST be calculated by subtracting the calculated |
| 128 | +commission from the block fee revenue. If the delegator rewards amount is |
| 129 | +non-zero, the vote account must be loaded and checked for the following |
| 130 | +conditions: |
| 131 | + |
| 132 | +1. account is vote program owned AND |
| 133 | +2. account is initialized with vote state v4 or later |
| 134 | + |
| 135 | +If the conditions are met, the delegator rewards amount MUST be added to the |
| 136 | +vote state field `pending_delegator_rewards` and added to the balance of vote |
| 137 | +account. If either of these conditions is violated, the delegator rewards amount |
| 138 | +MUST be burned. |
| 139 | + |
| 140 | +### Runtime: Delegator Rewards Distribution |
| 141 | + |
| 142 | +When calculating stake delegation rewards for a particular completed reward |
| 143 | +epoch, construct a list of all vote accounts that were initialized at the |
| 144 | +beginning of the reward epoch and had a non-zero active stake delegation. For |
| 145 | +each vote account, retrieve its state at the end of the reward epoch and check |
| 146 | +the `pending_delegator_rewards` field in its vote state. Let this value be `P`. |
| 147 | +If `P` is non-zero, use it to calculate rewards for each of the stake accounts |
| 148 | +delegated to the vote account as follows: |
| 149 | + |
| 150 | +1. Sum all active stake delegated to the vote account during the reward epoch |
| 151 | +epoch. Let this total be `A`. |
| 152 | + |
| 153 | +2. For each individual stake account, multiply its active stake from the |
| 154 | +reward epoch by `P`, and divide the result by `A` using integer division. |
| 155 | +Discard any fractional lamports. |
| 156 | + |
| 157 | +After calculating all individual stake rewards, sum them to obtain `D`, the |
| 158 | +total distribution amount. Because of integer division, the full amount `P` may |
| 159 | +not be distributed so compute the amount to be burned, `B`, as the difference |
| 160 | +between `P` and `D`. |
| 161 | + |
| 162 | +If no blocks in the epoch following the completed reward epoch have been |
| 163 | +processed yet, subtract `B` from both the vote account’s lamport balance and its |
| 164 | +`pending_delegator_rewards` field and store the updated vote account. Finally, |
| 165 | +the burn amount `B` should also be deducted from the cluster capitalization. |
| 166 | + |
| 167 | +#### Individual Delegator Reward |
| 168 | + |
| 169 | +The stake reward distribution amounts for each stake account calculated above |
| 170 | +can then be used to construct a list of stake reward entries which MUST be |
| 171 | +partitioned and distributed according to [SIMD-0118]. |
| 172 | + |
| 173 | +When reward entries are used to distribute rewards pool funds during partitioned |
| 174 | +rewards distribution, the delegated vote account for each rewarded stake account |
| 175 | +must have its `pending_delegator_rewards` field and its balance deducted with |
| 176 | +the amount of rewards distributed to keep capitalization consistent. |
| 177 | + |
| 178 | +[SIMD-0118]: https://github.com/solana-foundation/solana-improvement-documents/pull/118 |
| 179 | + |
| 180 | +### Vote Program |
| 181 | + |
| 182 | +#### Withdraw |
| 183 | + |
| 184 | +Since pending delegator rewards will be stored in the validator's vote account |
| 185 | +until distribution at the next epoch boundary, those funds will be unable to be |
| 186 | +withdrawn. |
| 187 | + |
| 188 | +The `Withdraw` instruction must be modified so that if the balance indicated by |
| 189 | +the `pending_delegator_rewards` field is non-zero, the vote account will no |
| 190 | +longer be closeable by fully withdrawing funds. The withdrawable balance when |
| 191 | +`pending_delegator_rewards` is non-zero will be equal to the vote account's |
| 192 | +balance minus `pending_delegator_rewards` and the minimum rent exempt balance. |
| 193 | + |
| 194 | +#### UpdateCommissionBps |
| 195 | + |
| 196 | +The `UpdateCommissionBps` instruction added in [SIMD-0291] must be updated to |
| 197 | +add support for updating the block revenue commission rate. |
| 198 | + |
| 199 | +When the specified commission kind is `CommissionKind::BlockRevenue`, update the |
| 200 | +`block_revenue_commission_bps` field instead of the previous behavior of |
| 201 | +returning an `InstructionError::InvalidInstructionData`. |
| 202 | + |
| 203 | +Note that the commission rate is allowed to be set and stored as any `u16` value |
| 204 | +but as detailed above, it will capped at 10,000 during the actual commission |
| 205 | +calculation. |
| 206 | + |
| 207 | +#### DepositDelegatorRewards |
| 208 | + |
| 209 | +A new instruction for distributing lamports to stake delegators will be added to |
| 210 | +the vote program with the enum discriminant value of `18u32` little endian |
| 211 | +encoded in the first 4 bytes. |
| 212 | + |
| 213 | +```rust |
| 214 | +pub enum VoteInstruction { |
| 215 | + /// # Account references |
| 216 | + /// 0. `[WRITE]` Vote account to be updated with the deposit |
| 217 | + /// 1. `[SIGNER, WRITE]` Source account for deposit funds |
| 218 | + DepositDelegatorRewards { // 18u32 |
| 219 | + deposit: u64, |
| 220 | + }, |
| 221 | +} |
| 222 | +``` |
| 223 | + |
| 224 | +Perform the following checks: |
| 225 | + |
| 226 | +- If the number of account inputs is less than 2, return |
| 227 | +`InstructionError::NotEnoughAccountKeys` |
| 228 | +- If the vote account (index `0`) fails to deserialize, return |
| 229 | +`InstructionError::InvalidAccountData` |
| 230 | +- If the vote account is not initialized with state version 4, return |
| 231 | +`InstructionError::InvalidAccountData` |
| 232 | + |
| 233 | +Then the processor should perform a system transfer CPI of `deposit` lamports |
| 234 | +from the source account (index `1`) to the vote account. Lastly, increment the |
| 235 | +`pending_delegator_rewards` value by `deposit`. |
| 236 | + |
| 237 | +## Impact |
| 238 | + |
| 239 | +Stake delegators will receive additional income when delegating to validators |
| 240 | +who adopt this new feature by setting a block revenue commission rate less than |
| 241 | +the default of `100%`. |
| 242 | + |
| 243 | +## Security Considerations |
| 244 | + |
| 245 | +NA |
| 246 | + |
| 247 | +## Backwards Compatibility |
| 248 | + |
| 249 | +A feature gate will be used to enable block reward distribution at an epoch |
| 250 | +boundary. |
0 commit comments