-
Notifications
You must be signed in to change notification settings - Fork 179
SIMD-0123: Block Revenue Distribution #123
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
SIMD-0123: Block Revenue Distribution #123
Conversation
72b12eb
to
0131156
Compare
Hello, I'm glad to see you looking in this direction. I wrote my draft on this subject, see SIMD 119, but this one is undoubtedly superior. I want to highlight that block fees are not the only amounts to be distributed. Validators collaborate with RPC pools, extract MEV from their blocks, and aim to compensate their stakers for the validators' downtime, among other things. Again, this would serve as a unified point for monitoring various dashboard sites (and stake pools) for validators' APY. Currently, the focus is solely on the commission percentage. However, as the methods of rewarding stakers evolve, I believe everyone will begin to operate with actual amounts. Thus, a straightforward and comprehensible algorithm for obtaining these data on-chain is essential. |
Thanks for chiming in @diman-io! I'm imagining that most fees including MEV tips will get accumulated into a single fee collection account for now but I'm interested to hear if there are use cases that require more flexibility. Looks like #109 will provide a convenient way to collect different types of fees into that account. Maybe it makes sense to track different types of fees/tips separately so that they can use different distribution methods but I think I would prefer to start with a simple solution that could be made more configurable in the future. Can you also elaborate more on why using commission might not be flexible enough? |
#109 is limited in this respect: its essence is to obtain the current leader's dependent account, otherwise, the goal cannot be achieved: the leader is not known in advance, the account must be locked for writing, potentially this account is needed by all block transactions (except for vote transactions), which blocks parallelism - in general, all this cannot be solved in any other way. Why commissions are not enough. Validators can attract borrowed stake, compensating the loan fees from the block fee. Thus, the percentage from the block fee cannot be the same for all stakers (or it will add many restrictions). About fee types. Yes, I've also thought about this for a long time. That's why in 119, I proposed several treasuries with different percentages. This adds flexibility in general. Is this necessary? I don't have a definitive answer. I don't think this will allow dashboards to track such revenues because if we move away from hardcoded typing and just allow validators to create multiple treasuries for one epoch, then for typed tracking to work, dashboards will have to know which treasury is of what type. Besides a textual description inside, I think nothing else can be invented. This rather gives validators the opportunity to keep their accounting on-chain, nothing more. On the other hand, it does not accuse the validator of anything, and he can still use off-chain calculation and himself fund the treasuries for the total amount to be distributed. After much contemplation, I've concluded that all this breaks down into two parts:
Part 1) is strongly associated with business processes, financial schemes within the validator's business, so it's hard to program all variants. But part 2) is clear and, as it seems to me, there are no disagreements among everyone about it. Moreover, part 2) immediately allows all dashboard sites to see the real profitability of the validator for stakers (i.e., the element that is very important for validators in their competitive fight for stakers) Returning to 109, honestly, I would go a different way: I would provide for the vote account 4 accounts: an account for paying for voting, an account for collecting base fees, an account for collecting priority fees, and an account for collecting tips (although personally, I don't like this name, I would prefer success fee or something like that). And then I would leave it up to the validator's discretion to transfer these fees into treasuries for distribution by the program from part 2). This will give flexibility to the validator in managing these amounts. The only issue that exists for me here is that the transfer might not occur in the current epoch, but in the next one. This bothers me from the standpoint of an ideal world scenario. On the other hand, looking at it as a business and in terms of attracting/retaining stake, transferring in the next epoch for the current one (meaning the staker will receive the amounts one epoch later), will retain stakers (because they will not receive additional amounts for the deactivation epoch - these accruals will happen after). So, overall from a business perspective, this is not a problem. And the validator is able to minimize it in the last few blocks of the epoch. I hope I was able to answer your questions. Structured presentation is not my strongest skill. |
So, speaking about 109, I'm more inclined towards some analogy of the ComputeBudgetProgram, but the fee collection would only occur if the transaction is successful. UPD. I'm not a proponent of 109 at all. In my opinion, validators will eventually concentrate MEV extraction in their own hands. |
see #109 |
in the discussion of #109, we come to the conclusion that we can basically have arbitrary, leader-defined distributions by implementing two much simpler things.
with these two things, a validator operators can set their collector to a program-controlled account and define distribution in any way they please. this creates a new competitive opportunity |
Great, my understanding was that #109 aims to solve the first simple thing (adding the syscall or instruction to credit the leader's collector account) so I think we're on the same page for that part. This SIMD aims to tackle the distribution part by allowing validators to create an independent collector account. I'm hopeful that we can have a discussion here about how to implement this in a flexible way without adding too much complexity. But as I mentioned above, there's a sticking point on using the vote account as you suggested because there's technically a one-to-many relationship of validator node id accounts to the vote account. Currently the collector account for a leader's bank comes from the node id, not the authorized voter key. When calculating the leader schedule we accumulate all the delegated stake for vote accounts and then accumulate all the vote stake according to each vote account's node id. Since we can't map from node id to a unique vote account, we can't lookup a validator defined collector account saved in vote state. That's why I created this proposal to allow validators to collect fees into a custom collector account which is derived from their node id. This proposal defines an opinionated distribution mechanism which piggy backs on stake reward distribution to distribute fees to delegated stakers. |
my contention is that the distribution mechanism need not be fixed by, nor imposed upon the protocol.
what prevents this? |
Is that so? I mean, I assume that this is exactly how it's implemented in the code of the validator creating the block (because before this, the right to create a block is checked under other conditions). UPD. I think you meant not the node id as such (which is broadcast in gossip). Regarding validator identity/authorized voter: I can look into this myself, there's no need to spend time on explanations. |
I assume that there might exist two vote accounts with the same validator identity/authorized voter. But I really like the idea of a separate instruction/syscall. |
there was more discussion around simd 109 alternatives on discord. i think there was some discussion on the topic prior to 109 being created as well, but i can't seem to find it at the moment. cc/ @buffalu |
Ok, understood. Seems like most people are in agreement so I'll revise that part. I think it's still useful to allow fees to be distributed to stakers if desired but that might be better suited for an amendment.
I've said multiple times now...
The protocol allows multiple vote accounts to set their node keys to the same validator node id. |
Would it make sense to rethink the linking of leader to node identity and rather make it linked to the vote account? The right to produce a block is conveyed directly by the active stake of the vote account. Block rewards could accrue through the epoch in a "suspense account" and be distributed with staking rewards, with a singular commission applied (let's also support non-integer commission values while we're at it?)? This also accomplishes the distribution of block fees as staked SOL with compounding and possibly helps economic security in that a leader who stuffs their own blocks won't immediately receive back their full priority fee (if SIMD-0096 passed) and half the base fee, but rather has to wait until epoch N+2 (given stake cooldown) |
Yeah, definitely worth exploring. |
Hey guys. What's the current status of this development? |
Just thinking about this and reading through the comments above, I'm thinking about the actual distribution mechanism and the impact this would have on the network. With the number of stake accounts already in existence, adding another process that distribute rewards to all of these would only exacerbate the current issues. In this regard would it be sensible to tie the distribution together, i.e. collect fees (as described above, in a collector account) and distribute at the same time (in the same process/block) as the inflation rewards? The share of distribution (i.e. commission) can be different, which only alters the amount debited/credited, but it means no additional debits or credits to any accounts (though it may be desirable to have them split for accounting purposes, but still within the same block). The only in-protocol revenue, consistent among all validators, are block fees and inflationary rewards, so I think MEV, side-deals etc should be outside of the scope of this proposal, though can be tacitly included if the collector account can receive arbitrary credits that are subject to the same distribution formula:
This also ensures compounding of rewards as they are distributed as activated stake The mapping of identity to vote account may still be problematic, especially where a validator changes their authorized voter, but this could be fixed within the same scope, possibly even changing the leader schedule to link to vote accounts (this actually feels like it should require its own SIMD). This proposal has been stale for some time, I'd like to see how we can revive this and move it forward, what are the blockers right now, is it just uncertainty over best implementation or some sort of hard blocker like inability to reliably link identity to vote account? |
@Benhawkins18 and I just had a discussion about this today FYI and we will be collaborating on determining how to best distribute block fees to stakers. This goal will mostly likely be split into multiple SIMDs to cover things like setting up collector accounts and commission rates, constraining validator ids to have a single vote account, and modifying the inflation stake reward distribution mechanism to support distributing collected fees. @michaelh-laine your latest comment matches our thinking quite closely, thanks for weighing in. |
- validator fee program: New core program which supports diverting a portion of | ||
block fees to stake delegators | ||
|
||
- validator fee account: New account type which supports setting a commission |
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.
Wdyt about having a separate SIMD just for this piece? This on its own is a valuable feature
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.
Yup, was already writing one up, here's a draft that you can check out. Would appreciate feedback from you and @buffalu. This SIMD now just focuses on 1) where to send commission from different revenue sources and 2) how to collect and distribute post-commission revenue to stake delegators
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.
oops SIMD draft here: #185
Long awaited update on this SIMD.. This SIMD now focuses on 1) how to set a commission rate for different revenue sources and 2) how to collect and distribute post-commission revenue to stakers via validator specific stake rewards pool accounts. There are a few prerequisite SIMDs...
I will also be drafting up a SIMD to design the syscall and/or system instruction for tipping the current leader |
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. |
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.
@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.
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.
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.
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.
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.
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.
Adding some minor comments
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. |
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.
Which SIMD is this referencing?
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.
Should have been SIMD-0185
Addressed feedback and made a few design changes:
|
@jstarry due to the latest changes made here, I have a question ( Will it be possible to add additional arbitrary lamports for the distribution? |
Currently we have a rather unique model where I manually calculate and give away 5% of our block rewards to animal welfare organizations each month. I think this SIMD could help. If they could be accrued in an arbitrary collection account and then (through DAO vote) transferred directly to a donation recipient's public donation address - that would be huge. Right now we have a bit of a "trust me bro" system. We've been audited in the past, but it's nonetheless not a great system for transparency. It would be fantastic if we could build a front-end that could show our acquired block rewards in a provable way, and show those transfers to animal welfare organizations on a front end. Right now, I'm tracking this with an Excel spreadsheet using formulas like it's 1995. My dream for a while has been to completely automate this process as much as possible. If this data was stored on chain, I could probably totally eliminate the spreadsheet. |
Yeah, planning to add a vote program instruction for that |
@bartenbach yes, that's the purpose of the Below is an excerpt from this SIMD which loosely describes this mechanism:
|
Fair, and logical. And smart to keep this onchain for many reasons above. |
The delegator rewards amount MUST be calculated by subtracting the calculated | ||
commission from the block fee revenue. If the delegator rewards amount is | ||
non-zero, it must be added to the vote state field `pending_delegator_rewards` | ||
and added to the balance of vote account. |
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 won't work currently because the vote account could have been closed by the time the validator produces their blocks. Vote accounts are close-able as long as they haven't earned any vote credits in both the current epoch or previous epoch. We might need to amend the vote program to prevent closing vote accounts that are part of the leader schedule.
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 think the best way to handle this edge case is to burn any delegator rewards that can't be recorded in an active vote account. Delegators that observe that their delegated vote account hasn't been voting for an epoch have time to redelegate and should do so because they will be missing out on inflation rewards anyways.
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.
We might need to amend the vote program to prevent closing vote accounts that are part of the leader schedule.
i think there are other ways we can investigate to synchronize vote account liveness with the leader schedule
I think the best way to handle this edge case is to burn any delegator rewards that can't be recorded in an active vote account. Delegators that observe that their delegated vote account hasn't been voting for an epoch have time to redelegate and should do so because they will be missing out on inflation rewards anyways.
burn seems fine for now. inattentive stake is nothing to be rewarded
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.
Addressed in 9ae2000
Here's the dependency graph of simd's: graph TD
%% Layer 1 - Base dependencies
simd_0180["simd-0180<br/>vote keyed leader schedule"]
simd_0185["simd-0185<br/>vote account v4"]
simd_0249["simd-0249<br/>delay commission updates"]
%% Layer 2 - Mid-tier proposals
simd_0232["simd-0232<br/>custom commission addresses"]
simd_0291["simd-0291<br/>commission rate in basis points"]
simd_0232 --> simd_0180
simd_0232 --> simd_0185
simd_0291 --> simd_0185
simd_0291 --> simd_0249
%% Layer 3 - Top-level proposal
simd_0123["simd-0123<br/>block revenue sharing"]
simd_0123 --> simd_0180
simd_0123 --> simd_0185
simd_0123 --> simd_0232
simd_0123 --> simd_0291
|
When calculating stake delegation rewards for a particular completed reward | ||
epoch, construct a list of all vote accounts that were initialized at the | ||
beginning of the reward epoch and had a non-zero active stake delegation. For | ||
each vote account, retrieve its state at the end of the reward epoch and check | ||
the `pending_delegator_rewards` field in its vote state. Let this value be `P`. | ||
If `P` is non-zero, use it to calculate rewards for each of the stake accounts | ||
delegated to the vote account as follows: |
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.
It’s not very clear here, especially if you’re not a native speaker, I suspect. The version that refers to the previous epoch, the current epoch, and the first block of the current epoch is more unambiguous. Variants: the epoch for which rewards are earned - rewarded epoch, the epoch in which rewards are distributed - distribution epoch. Or you can use N and N-1 terminology for clarity. I generally prefer the approach used in SIMD-0118. It clearly defines what and when is taken from the state, what load from the snapshot (and save to), and how it all fits together.
If no blocks in the epoch following the completed reward epoch have been | ||
processed yet, subtract `B` from both the vote account’s lamport balance and its | ||
`pending_delegator_rewards` field and store the updated vote account. Finally, | ||
the burn amount `B` should also be deducted from the cluster capitalization. |
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 don’t think this is correct, or I didn’t understand the logic here.
Look,
7 * [0.1, 0.9] = [0.7, 6.3] -> [0, 6] -> 6
6 * [0.1, 0.9] = [0.6, 5.4] -> [0, 5] -> 5
Secondly, when we calculate rewards for stake accounts, we simply increase capitalization at the same moment. That is, if for some reason, by the time of calculation, the stake account or vote account no longer exists, it just won’t increase the capitalization (which reduces real inflation). However, in the case of the vote account, this will lead to unfair distribution (some stake accounts of the removed vote account may still manage to receive their rewards).
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. This way, we can avoid the error of double discarding the fractional part (which I described above), reduce the number of accounts written to in each block, and overall allows us to lift the restriction on deleting a vote account before distribution is complete (I think this was mentioned in the description of the v4 vote program).
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.
- There isn't any double discarding. We only discard leftover lamports when portioning out the pending rewards to each stake account.
- Yes, in addition to writing all the stake accounts for a partition, we will also modify all of the vote accounts that were delegated to by those stake accounts. We could just write all vote accounts once at the boundary and move the pending rewards into a sysvar as you suggested. I like that better actually, I'll write up an amendment proposal.
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.
Perhaps I wasn’t entirely precise. The two-line example above is actually a single example. If there were only one field pending_delegator_rewards, it wouldn’t be sufficient. The example assumes a total of 7 lamports to be distributed and two stakers with shares of 0.1 and 0.9 for the validator. If calculated at the beginning of the epoch, the stakers would receive 0 and 6 lamports, respectively. And we would immediately decrease pending_delegator_rewards by 1 lamport. But then, during actual distribution, we would get 0 and 5 lamports due to truncation. And 1 lamport would remain undistributed.
To avoid this, we need to store the original value of pending_delegator_rewards.
Thus, we need two fields - one to store new rewards for the current epoch and another to store the original value from the previous epoch. The remaining amount to be distributed is not strictly necessary.
In any case, using a sysvar moves these problems out of the vote account state. The sysvar data can store only the original unmodified amount, and the balance transfer from the vote account can be done using the reduced value.
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.
We use the original value of pending rewards from the epoch boundary for each of the stake account calculations
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.
Yes, yes, I understand. But the entire paragraph refers to “If no blocks in the epoch…” and then “…and store the updated vote account”. So it looks like the actual value will already be lost by the second block of the epoch. But the actual funding of stake accounts will happen later. And at that point, the original value will be needed again (because without it, the error I described in the example will occur). And yes, I’m assuming here that this value is stored somewhere in the state, not just in the Banks at validator runtime.
But I saw the new thread. I think it makes sense to move the discussion there.
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.
Got approvals from all teams, governance passed. Merging
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. This proposal introduces a way to set a commission for validator collected block fees and tips and then have the rest of the fees distributed to delegated stake accounts.