Skip to content

Conversation

@jstarry
Copy link
Contributor

@jstarry jstarry commented Jun 9, 2025

In the case of additional rewards, we move amounts from the vote account to the stake accounts. By the way, this will lead to writing all vote accounts every block (assuming the partition reward distribution is uniformly distributed), right?

I think it’s better to follow the approach of SIMD-0118, but we’ll have to use a sysvar for that (since capitalization is not increased). That is, in the first block of the epoch, record the reward distribution amounts by vote accounts into the sysvar data, and move that amount to the sysvar.

Originally posted by @dimandotsol in #123 (comment)

@jstarry jstarry marked this pull request as draft June 9, 2025 22:14
@jstarry jstarry force-pushed the amend-block-distribution branch from a3dc7d2 to 1807d1e Compare August 1, 2025 03:59
@jstarry jstarry marked this pull request as ready for review August 1, 2025 04:00
@jstarry jstarry force-pushed the amend-block-distribution branch from 1807d1e to 6c6cda1 Compare August 1, 2025 04:03
@jstarry jstarry requested review from HaoranYi and t-nelson August 1, 2025 04:03
@jstarry
Copy link
Contributor Author

jstarry commented Aug 1, 2025

@dimandotsol can you take a look now and see if the distribution section is easier to understand?

@dimandotsol
Copy link

@dimandotsol can you take a look now and see if the distribution section is easier to understand?

Looks great! Thank you!

@dimandotsol
Copy link

dimandotsol commented Aug 1, 2025

I noticed that lamports will remain on the sysvar balance due to integer division. Maybe it’s worth explicitly mentioning this?

In general, it’s fine, and how to handle it can be addressed later. But while reading, I had the following idea:

SysvarData {
  total_rewards: u64, // sum of all P
  total_distributed: u64,
  validators: [
    ValidatorRewardParams {
      vote_account: Pubkey,
      rewards: u64,       // P
      active_stake: u64,  // A
    }
  ]
}

And on each distribution block, increase total_distributed by the actual amount deducted.
At the end of the distribution, burn total_rewards - total_distributed.
(Technically, total_rewards isn’t necessary here, since it’s the sum of rewards in the validators array.)

@HaoranYi
Copy link
Contributor

HaoranYi commented Aug 1, 2025

The idea sounds good to me intuitively. However, there are a few more details that need to be fleshed out.

  1. If we clear pending_rewards from the vote accounts and only store the sum in sysvar in the 1st block, how do we recover the payout for each stake delegation later when distribution?
  2. How do we handle restart during reward distribution? We need to recompute the payout for each delegation but the orignal pending_rewards on the vote accounts is not available?
  3. If we store ValidatorRewardParams in sysvar, it will be too big ~hundreds of Mega bytes. That will be too big for a sysvar data. It seems like this is not a good option.
  4. Have we considered the impact of computing all the stake delegation at the first block. Will this make the already-busy first bock even more busy?
  5. If we do in the original way, pending_rewards in the vote account may change before we distribute the payout, because new block are played and rewards between first_epoch block till distribution block will be accumulated into pending_rewards. When we distribute, we will include the new rewards for current epoch?

@dimandotsol
Copy link

dimandotsol commented Aug 2, 2025

  1. We’re adding this to the partitioned inflation rewards distribution algorithm that’s already working (SIMD-118):
    active_stake_from_stake_account * this_validator_reward_from_sysvar / this_validator_active_stake_from_sysvar
    (where active_stake_from_stake_account / this_validator_active_stake_from_sysvar is the share of a specific stake account in the additional rewards of the given validator)

  2. We don’t need to do anything directly in this SIMD. SIMD-118 will continue to function, and so will we alongside it.

  3. Only for ~2000 validators: 2000 * (32 + 8 + 8) + 8 (array length) (+ 8 or 16 with my idea above). So it's about 100 kilobytes.

  4. Only a “transfer” of the amounts for distribution from vote accounts to the sysvar.

  5. I’ve already forgotten, but I think the second field was proposed to be used there.

@jstarry
Copy link
Contributor Author

jstarry commented Aug 8, 2025

I noticed that lamports will remain on the sysvar balance due to integer division. Maybe it’s worth explicitly mentioning this? [..] At the end of the distribution, burn total_rewards - total_distributed.

@dimandotsol Thanks for pointing this out, agreed on burning the excess, just amended!

If we clear pending_rewards from the vote accounts and only store the sum in sysvar in the 1st block, how do we recover the payout for each stake delegation later when distribution?

@HaoranYi We recover this value from the epoch stakes cache. We already use the epoch stakes cache for recalculating partitioned inflation rewards after restart. In the future we can store the last few epochs of delegator rewards in the vote account state so that we don't need to rely on the epoch stakes cache.

Have we considered the impact of computing all the stake delegation at the first block. Will this make the already-busy first bock even more busy?

Yes, have considered it. Basically it's just a few extra arithmetic operations to calculate the extra rewards.

If we store ValidatorRewardParams in sysvar, it will be too big ~hundreds of Mega bytes. That will be too big for a sysvar data. It seems like this is not a good option.

@HaoranYi agreed. I don't think it's a good option and not necessary as I explained above. @dimandotsol please note that I didn't include this part in the amendment.

@dimandotsol
Copy link

@dimandotsol please note that I didn't include this part in the amendment.

@jstarry Wasn’t the validators array supposed to be stored in the sysvar data? Those are just the P and A needed for the calculations

validators: [
    ValidatorRewardParams {
      vote_account: Pubkey,
      rewards: u64,       // P
      active_stake: u64,  // A
    }
  ]

And that’s less than 100 KB for 2000 validators.

#### Delegator Rewards Completion

After distributing all partitioned delegator rewards, the epoch rewards sysvar balance
MUST be reset to its rent exemption balance and any surplus lamports are burned.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach will allow burning any amount of lamports that can be transferred to the sysvar address.

By simply additionally storing values in only two u64 fields (in fact, one is enough, because the second is just the sum of P over the array and computing it once isn’t a problem), I’m proposing to prevent burning of arbitrary amounts - only rounding burns would remain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't transfer to the sysvar in a transaction, it's reserved

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay, I didn’t know that.

@jstarry
Copy link
Contributor Author

jstarry commented Aug 11, 2025

Wasn’t the validators array supposed to be stored in the sysvar data? Those are just the P and A needed for the calculations

Nope, the calculated values do not to be stored onchain, this SIMD is designed so that those values can always be recalculated after restart.

@HaoranYi
Copy link
Contributor

It seems to me that the complexity of introducing a new sysvar for block
revenue payout in this spec may not be worth it for the following reasons.

  1. From what I understand, the main motivation of introducing the sysavar is to
    avoid update vote accounts for reward paying blocks. However, most of of those
    vote accounts are likely to be updated anyways per block due to voting
    transactions. And the payout reward update to vote accounts will happens in
    write-cache. Extra update in write cache is not expensive.

  2. If we avoid creating this sysvar, we can avoid computing the delegation
    rewards at the first block of the epoch, which will be nice.

@HaoranYi
Copy link
Contributor

I was confused by the comments and the actual SIMD.

The SIMD didn't introduce a new sysvar. So my last comments should be like this.

It seems to me that the complexity of introducing a new sysvar for block revenue payout in this spec may not be worth it for the following reasons.

t seems to me that the complexity of collecting rewards into epoch reward sysvar for block revenue payout in this spec may not be worth it for the following reasons.

Copy link
Contributor

@HaoranYi HaoranYi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm

Comment on lines +168 to +171
Note that unlike inflation rewards distribution, block revenue distribution will
not impact any internal epoch rewards sysvar state fields like `total_rewards`
or `distributed_rewards` since block revenue will instead be tracked via the
epoch rewards sysvar lamport balance.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels hacky and could lead to edge cases - how do we ensure that the sysvar account is rent-exempt, for example? Is it that bad to just add a field to the sysvar? Overloading the lamport balance feels like it could be confusing, is there a good reason for doing this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the reward lamports already exist onchain they need to live somewhere, unlike inflation rewards which are newly created for distribution. Adding another field might lead to more edge cases since we would need to track both the lamport balance and the field balance. The sysvar account already always exists so no need to worry about rent exempt balance here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The capatialization thing makes sense, thanks! It would be great to get a bit more clarification on how this interacts with the code for making sysvars always rent exempt.

When sysvars are saved, update_sysvar_account calls adjust_sysvar_balance_for_rent which ensures the balance is always rent-exempt. Are you proposing that the lamport balance of the epoch reward sysvar would be minimum_balance_for_rent_exemption + the reward lamports?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you proposing that the lamport balance of the epoch reward sysvar would be minimum_balance_for_rent_exemption + the reward lamports?

Yes, that's correct. On all clusters the sysvar already exists with the rent exempt balance, so this SIMD doesn't detail anything about topping up the sysvar account balance.

Btw I talk about the sysvar balance a bit more a little lower in the SIMD

After distributing all partitioned delegator rewards, the epoch rewards sysvar balance MUST be reset to its rent exemption balance and any surplus lamports are burned.

@jstarry
Copy link
Contributor Author

jstarry commented Sep 11, 2025

@t-nelson @topointon-jump @buffalojoec @Benhawkins18

I think I spent too much energy thinking about the distribution mechanism and not enough about what we're actually sharing. As written, SIMD-0123 will allow validators to set a commission for ALL block revenue and the rest goes to delegators. This means that EVEN base fees will be shared with delegators. This feels wrong to me. We should only support sharing priority fees right? Base fees in theory pay for the base cost of tx compute?

@Benhawkins18
Copy link
Collaborator

I lean towards keeping this simple: all block revenue (both base fees and priority fees) should flow through the same commission mechanism. Validators already have multiple potential revenue streams, and they can decide what to share (or not share) via their overall commission.

Introducing a separate carve-out just for base fees adds complexity for both validators and delegators. Delegators would need to understand that “commission” applies differently depending on the source of the revenue, which feels like unnecessary nuance. If everything is bundled, the mental model is much cleaner: delegators know they’re sharing in all block rewards minus validator commission, and validators retain the flexibility to share other income streams however they see fit.

So my preference is that SIMD-0123’s “block reward commission” applies uniformly across both base fees and prioritization fees.

@tao-stones
Copy link
Contributor

I think I spent too much energy thinking about the distribution mechanism and not enough about what we're actually sharing. As written, SIMD-0123 will allow validators to set a commission for ALL block revenue and the rest goes to delegators. This means that EVEN base fees will be shared with delegators. This feels wrong to me. We should only support sharing priority fees right? Base fees in theory pay for the base cost of tx compute?

Conceptually I like the separation of base fee vs. priority fee from the revenue-sharing mechanism.

  • If we treat the base fee as covering operational costs, it feels more right for validators to share “profit” rather than “revenue”

  • That said, delegators do help secure leader slots, which in turn increases base fee collection, so it’s not entirely wrong to share base fees too.

  • Personally I lean toward sharing priority fees only (thinking alone the lines of profit sharing), it doesn’t seem to introduce too much complexity.

@tigarcia
Copy link
Contributor

Isn't another motivation for SIMD-123 to allow operators to send fees to other keys besides the identity if they choose? It seems wrong to force them into sending it to the identity if they don't want to.

@t-nelson
Copy link
Contributor

do not discern priority fees. they need to die and we should make that as easy as possible

@jstarry
Copy link
Contributor Author

jstarry commented Sep 24, 2025

Isn't another motivation for SIMD-123 to allow operators to send fees to other keys besides the identity if they choose? It seems wrong to force them into sending it to the identity if they don't want to.

Yes, that is something we are doing but it's technically part of #232. That SIMD does refer to the new block_revenue_collector account as a "commission" account but the base fees would all get collected there even if they were not part of delegator rewards distribution. So that shouldn't be a concern.

If we treat the base fee as covering operational costs, it feels more right for validators to share “profit” rather than “revenue”

This is what I was thinking as well. But now I'm realizing this doesn't make sense. Each transaction is processed by all the validators and if base fees were truly meant to cover cluster operational costs, they would get divided evenly (regardless of stake) to all validators rather than just getting paid to the current leader.

So now I'm thinking @Benhawkins18 and @t-nelson have the right idea that we shouldn't differentiate between base and priority fees here. It's all block revenue. We can always differentiate later if we really need to but I think it makes most sense to combine them for block revenue sharing with delegators. The whole point of a commission on block revenue (and collection to a specific collector address for bespoke solutions) is for validators to adjust how much revenue makes sense for their operational strategy rather than rely on base fees.

@buffalojoec
Copy link
Contributor

buffalojoec commented Sep 24, 2025

Just throwing my two cents in here since I was tagged!

I think the drawback of including both the base fees and the priority fees in the block commission is that ecosystem "standards" for evaluating a validator's commission could give larger validators a competitive edge.

With the fees combined, imagine a scenario where potential stakers are evaluating which node to stake with: a small node and a large node.

  • The small node relies on some block revenue to survive, so they can't set a commission below N%.
  • The large node has other revenue streams (ie. MEV) it can lean on to allow them to present a block commission of 0%.

To the uninformed retail staker, this might be grounds to stake with the larger node, to save that N%. Thus, the larger node can more effectively leverage low block commission rates to attract stake and increase their MEV profit.

I think keeping base fees separate could help level the playing field a bit. The small node could safely rely on base fee revenue, while still advertising a 0% block commission just like the large node.

@jstarry
Copy link
Contributor Author

jstarry commented Sep 24, 2025

Yeah but small stake nodes have small base fee revenue because you only earn base fees for blocks that you produce. Sure you will get compensated for executing transactions in your block, but that's not going to be enough to cover operational costs unless you set non-zero commission or have other revenue streams

@buffalojoec
Copy link
Contributor

Yeah but small stake nodes have small base fee revenue because you only earn base fees for blocks that you produce. Sure you will get compensated for executing transactions in your block, but that's not going to be enough to cover operational costs unless you set non-zero commission or have other revenue streams

That's until you attract enough stake though. Base fee compensation would increase if you're competitively attracting new stake. It could be acceptable to operate at a diminishing loss for a bit as you grow your stake.

To be fair, this may not be a strong enough argument for splitting them, since it would mean we'd have less flexibility to redesign block revenue characteristics and we'd be stuck with another lengthy protocol change if enough operators request the ability to distribute all block revenue, but it's worth at least considering IMO.

@solana-foundation solana-foundation deleted a comment from kronismarpa Nov 14, 2025
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

Successfully merging this pull request may close these issues.

9 participants