Skip to content
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions proposals/0123-block-revenue-distribution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
---
simd: '0123'
title: Block Revenue Sharing
authors: Justin Starry (Anza)
category: Standard
type: Core
status: Review
created: 2024-03-10
feature: (fill in with feature tracking issues once accepted)
---

## Summary

A new mechanism for is proposed to allow validators to share part of their block
fee and tip revenue with their delegators. Commission rates from validator vote
accounts will be used by the protocol to calculate post-commission rewards that
will be automatically distributed to delegated stake accounts at the end of each
epoch.

## Motivation

Delegated stake directly increases the number of blocks that a validator is
allocated in an epoch leader schedule but the core protocol doesn't support
diverting any of that extra revenue to stake delegators.

## Alternatives Considered

### Out of protocol reward distribution

Due to the lack of core protocol support for distributing block revenue to
stakers, validators have developed their own solutions which are not enforced by
the core protocol. For example, the Cogent validator diverts part of its fee
revenue to NFT holders. But it's up the NFT holders to audit and hold Cogent
accountable to a specific commission rate.

Another alternative is Jito's mechanism for block "tips" (not fees, but the idea
is similar). Jito's validator implementation includes a tip distribution program
which it instructs validator operators to divert all of their tips to but cannot
enforce perfect compliance. It's up to stakers and the Jito team to audit
compliance by validator operators. This mechanism requires trusting a
third-party (in this case Jito) to calculate reward distribution in an accurate
and fair manner. It also relies on using a merkle tree to distribute fees to
all stake accounts and the distributed fees are not automatically staked in
recipient stake accounts.

### Partitioned reward calculation

The proposed design requires that all stake rewards pool accounts are updated
before processing any transactions in the first block of an epoch. This adds
more computation overhead at epoch boundaries but this overhead is directly
proportional to the number of vote accounts which are similarly updated at the
beginning of each epoch when calculating stake inflation rewards. If this
calculation overhead becomes an issue, the reward bookkeeping in stake rewards
pool accounts will need to be modified so that rewards for the new epoch do not
interfere will reward calculations for the previous epoch.

## New Terminology

Stake Rewards Pool Account: An account that records how many rewards from block
fees and tips earned by a particular validator will be distributed to stake
delegators at the end of an epoch.

## Detailed Design

### Block Revenue Collection

After all transactions are processed in a block for a given leader, rather than
collecting all block revenue into the validator identity account, the protocol
will check if the validator's vote account has specified commission rates and
collector addresses in the new vote account version described in SIMD-XXXX.
Copy link
Contributor

Choose a reason for hiding this comment

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

Which SIMD is this referencing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should have been SIMD-0185

Block revenue is composed of two revenue sources: block fees and block tips,
which are configured by validators independently.

If the commission rate and collector account for a revenue source aren't set,
all revenue will be collected into the validator's identity account as before.
If the commission rate and collector account for a revenue source *are*
specified, the commission amount MUST be calculated by first multiplying the
rate by the amount of revenue and then using integer division to divide by one
hundred. Then the commission amount should be deposited into the specified
collector address ONLY if the collector account is system program owned and
would be rent-exempt after the deposit.
Copy link
Contributor Author

@jstarry jstarry Oct 17, 2024

Choose a reason for hiding this comment

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

@michaelh-laine you previously wrote

When rewards distribution occurs the commission is applied to the collector account funds (commission paid to vote account) and balance included with rewards paid into stake accounts

But I think it would be better for validators if they could receive block fee revenue immediately after each block in order to have a stream of income to pay for voting costs rather than needing to wait for epoch boundaries. With this approach, an important thing to consider is how to design commission rate updates because it would be a little strange to allow commission to change multiple times throughout the epoch. Maybe the vote program should only support setting the commission for the upcoming epoch so that the current epoch commission rates are always fixed from the beginning of an epoch.

Choose a reason for hiding this comment

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

At most a validator would need to frontload 2 SOL to pay an epoch's voting fees, but I don't feel strongly about that component. Commission change should not be permitted after 50% of the epoch has elapsed the same as it is for staking and should apply for an entire epoch, agreed there.

Your proposal states commission should be paid to a system program-owned account, but I think a major use case here would be for larger and institutional validators to automatically divert those funds into a multisig for security and opsec purposes - this then naturally isn't a system-program owned account.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Commission change should not be permitted after 50% of the epoch has elapsed the same as it is for staking and should apply for an entire epoch, agreed there.

Hoping to make commission related updates only apply for the following epoch FYI. Will be updating #185 to reflect that design.

Your proposal states commission should be paid to a system program-owned account, but I think a major use case here would be for larger and institutional validators to automatically divert those funds into a multisig for security and opsec purposes - this then naturally isn't a system-program owned account.

Note that since you can define a system-program owned account that isn't your node validator id, it doesn't need to have a private key anymore and so you could use a PDA which is controlled by a multisig or other onchain program.


For each revenue source, the amount of rewards that will be distributed to stake
delegators is calculated by subtracting the commission from the block revenue.
This amount will be deposited in a stake rewards pool account derived from the
block producer's vote address.

If the reward distribution amount is non-zero, a stake rewards pool account
address should be derived from the validator's vote account and loaded from
accounts db. The address MUST be derived with the following derivation seeds
for the stake program:

```rust
let rewards_pool_address = Pubkey::create_program_address(
[
b"stake_rewards_pool",
vote_pubkey.as_ref(),
&[vote_account.rewards_pool_bump_seed],
],
&stake_program::id(),
);
```

If a derived stake rewards pool account doesn't already exist or has not been
initialized, it should be created with the stake program as its owner and a data
size of 4 bytes. Its balance should be initialized with a rent exempt balance
which will increase total cluster capitalization similar to how sysvars are
created. Its data should be initialized with the `StakeState::RewardsPool` enum
variant discriminant `3u32` little endian encoded.

```rust
pub enum StakeState {
Uninitialized,
Initialized(Meta),
Stake(Meta, Stake, StakeFlags),
RewardsPool,
}
```

Once the derived stake rewards pool account exists and is initialized along with
a rent exempt balance, its balance should be increased by the amount of
post-commission revenue for each revenue source.

### Block Fee and Tip Distribution

At the beginning of an epoch, for each unique vote account in the previous
epoch's leader schedule, the protocol REQUIRES checking if a derived stake
rewards pool account exists, is initialized, and has a lamport surplus above its
rent-exempt balance. For every stake rewards pool with a lamport surplus, the
lamport surplus is divided evenly by delegated stake weight across all stake
accounts and these amounts will be included into a list of reward entries which
MUST be partitioned and distributed according to SIMD-0118.

When reward entries are used to distribute rewards pool funds during paritioned
rewards distribution, the stake rewards pool account balance must be deducted
with the amount of rewards distributed to keep capitalization consistent.

## Impact

Stake delegators will receive additional stake reward income when delegating to
validators who adopt this new feature.

## Security Considerations

NA

## Backwards Compatibility

A feature gate will be used to enable block reward collection and distribution
at an epoch boundary.