Skip to content
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

RFC-0005: Protocol Upgrade to Enable Automatic Distribution of Delegation Rewards #9

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

lampardlamps
Copy link

No description provided.

… Delegation Rewards to RFC-0005: Protocol Upgrade to Enable Automatic Distribution of Delegation Rewards.md
@lampardlamps lampardlamps requested a review from a team as a code owner December 26, 2023 19:39
@teddyjfpender
Copy link
Contributor

Thanks for making this PR @lampardlamps! 🙏

@es92
Copy link
Collaborator

es92 commented Jan 5, 2024

Just wanted to surface here, I think there was a previous plan, to (1) add binding coinbase receiver to zkApps, and then (2) have a zkApp that can manage payouts (probably yeah based on the work Gareth had done). Do we think that would be sufficient, or is more necessary? Building a complete version of that zkApp could probably be a good grant to come out of this (and something that could go into production after hard fork launch, though a fully constrained version would have to wait until coinbase receiver is added in)

… Delegation Rewards.md

updated RFC based on Teddy's comments
@lampardlamps
Copy link
Author

Just wanted to surface here, I think there was a previous plan, to (1) add binding coinbase receiver to zkApps, and then (2) have a zkApp that can manage payouts (probably yeah based on the work Gareth had done). Do we think that would be sufficient, or is more necessary? Building a complete version of that zkApp could probably be a good grant to come out of this (and something that could go into production after hard fork launch, though a fully constrained version would have to wait until coinbase receiver is added in)

Hi Evan, thanks for your comments. I was not involved in the details of the discussion. Was it determined that implementing a zkApp for this purpose would be the better choice than one on the protocol level? If so, the zkApp should be okay as long as it satisfies the requirements outlined here, namely: 1. the block producer do not have direct access to the block rewards; 2. the delegation charges are configurable onchain; 3. the rewards for each user is directly distributed/ available for claim according to the charge configurations.

@lampardlamps
Copy link
Author

@teddyjfpender thanks for your comments Teddy, I've made some changes accordingly, I hope they are acceptable.

@es92
Copy link
Collaborator

es92 commented Jan 9, 2024

Just wanted to surface here, I think there was a previous plan, to (1) add binding coinbase receiver to zkApps, and then (2) have a zkApp that can manage payouts (probably yeah based on the work Gareth had done). Do we think that would be sufficient, or is more necessary? Building a complete version of that zkApp could probably be a good grant to come out of this (and something that could go into production after hard fork launch, though a fully constrained version would have to wait until coinbase receiver is added in)

Hi Evan, thanks for your comments. I was not involved in the details of the discussion. Was it determined that implementing a zkApp for this purpose would be the better choice than one on the protocol level? If so, the zkApp should be okay as long as it satisfies the requirements outlined here, namely: 1. the block producer do not have direct access to the block rewards; 2. the delegation charges are configurable onchain; 3. the rewards for each user is directly distributed/ available for claim according to the charge configurations.

Yep I think it would match those requirements - and yep the thought is if it can meet requirements it would be better this way than at the protocol level, since this way features can be added / removed as needed by BPs / staking pools, and without requiring HFs.

@lampardlamps
Copy link
Author

Yep I think it would match those requirements - and yep the thought is if it can meet requirements it would be better this way than at the protocol level, since this way features can be added / removed as needed by BPs / staking pools, and without requiring HFs.

Thanks Evan, makes perfect sense!

@EmrePiconbello
Copy link

I think Gareth should get involved in this conversation. I was a follower of that project and the last update I got "because of reliance on oracles. It's not worth it because of risks" I might be recalling details wrong.

@garethtdavies
Copy link

garethtdavies commented Jan 31, 2024

I think Gareth should get involved in this conversation. I was a follower of that project and the last update I got "because of reliance on oracles. It's not worth it because of risks" I might be recalling details wrong.

The big blocker was the lack of coinbase receiver binding - as anyone could just switch the coinbase receiver and not use the zkApp, which makes the solution kind of moot. As I understand at this time, there won't be an option to make the contract immutable, i.e., permissions: None, which again means that it wouldn't be permissionless (I think this is just temporary as the crypto is likely to change). Finally, as this relies on an oracle, this would need to be very robust - I'd started some research on recursively validating multiple signatures from different oracles (circuit size was an issue otherwise, though things may have changed). So my comments there were more around launching something like this right after the hard fork, which isn't currently very feasible, or at least the benefits don't outweigh the risks, rather than long-term.

@jrwashburn
Copy link

While the theory of payout creativity was an exciting idea, the reality is that it's been pretty limited. This is not some application-layer capability - it's directly related to the security of the blockchain and should be treated as such. I don't think there is much upside to a bunch of creativity in this area. A zkapp/oracle-dependent solution is a workaround, and while better than our current work-arounds, this should be implemented by the protocol.

@michal0mina
Copy link

michal0mina commented Feb 8, 2024

Intro

@lampardlamps Thank you for the proposal and writing up the RFC! It is clear that this addition is a necessary component of the protocol and the ecosystem.

That said, I find it quite high level. I think we should list various options with more specific details at a lower level. I propose we continue this comment thread to explore and refine potential solutions.

I know that @garethtdavies was quite active in this area (thank you!), this is his zkApp description: https://hackmd.io/@garethtdavies/BJH3xMpFs

Myself -- I am quite familiar with classic dPoS protocol solutions, which center around on-chain, in-protocol, in-daemon implementations. I list it below as the first one, but frankly, I think we should look for alternatives to leverage the power of ZK stack.

Each of the solutions listed below may require breaking changes, but the extent of them is very different and depends on specific details.

High level list of potential solutions

1. Daemon extension for on-chain protocol rewards distribution

This is quite straight forward:

  • Stake delegation/undelegation is covered with the consensus like any other transaction
  • It allows to build up additional structures tracking relations between BPs and Delegators
    • probably requires BP registration to allow them register BP's fee
  • Daemon implementation updates respective delegators accounts depending on won blocks
    • need to specify options for re-delegating rewards or paying back (tax issues)
    • account balances are updates implicitly
  • Daemon maintains this information for current and next epoch
  • NICE TO HAVE: Delegators specify if they wish to accumulate or receive rewards immediately [UPDATE, 12.02., cc: @jrwashburn ]
  • Maybe we should update balances every block [UPDATE, 13.02, cc: @Sventimir]. It removes pressure from end of epoch balance update. This is something to specify and load test. Updating every block makes it hard to track balances in large merkle trie structures (as it requires rehashing whole tree).

UNKNOWN:

  • do we need to change blockchain tip circuit to make it work?
  • can we maybe extend coinbase transaction with the new type of batch transaction which lists all of those updates (if we cannot make it implicit change in the background)

PROS:

  • Fully automatic. Everything happens within daemon and BPs don't need to maintain another execution context for rewards
  • Inherits trustlessness and decentralization from the main chain [UPDATE, 12.02, cc: @EmrePiconbello]
  • Avoids custody problems (tax and e-money license issues) [UPDATE, 12.02, cc: @EmrePiconbello]
  • Goes along classic delegation designs in dPoS setup [UPDATE, 12.02, cc: @EmrePiconbello]
    • it is well understood
    • new regulations will cover it

CONS:

  • Daemon must maintain two large in-memory structures
    - current delegations and next epoch delegations [UPDATE, 13.02, cc: @Sventimir]
    • there will also be another new structures too keep config per BP
  • Daemon gets very busy at the epoch boundaries [UPDATE, 13.02, cc: @Sventimir]
  • If the rewards are not expressed as transactions (usually it's not necessary and saves block space), it will complicate Rosetta implementation
  • ...and also it's harder to write tests due to implicit changes
  • Not leveraging offchain capabilities of Mina

2. zkApp DISTRIBUTING the rewards

I believe this case captures Gareths implementaiton.

This is zkApp which:

  • Receives block information
    • oracle dependency
  • It goes through the data and builds up in optimised way transactions to transfer rewards from BP to Delegators (minus fee)
  • The app nicely lists and secures configuration parameters (fee, keys, etc)

PROS:

  • off chain computation
  • explicit transactions for applying rewards occupy minimal possible block space

CONS:

  • Dependency on an oracle(s)
  • Explicit transactions (as they take block space)
  • It's not trustless, because a BP can change coinbase recipient any time. However that's a case in any solution
  • Concurrent updates issue. [CONCERN FOR ALL -- details below]

3. zkApp ACCUMULATING the rewards

As an alternative lets consider an app:

  • A zkApp address is registered as rewards receiver
  • Delegators delegate to it
  • The zkApp maintains internal state of Delegators for the current and next epoch
  • Accounts are updated only on request
  • The app keeps root hash of the data on-chain

PROS

  • off-chain, zkApp
  • it has more long term nature, maybe even resembles financial instrument?
    • not a payment router
    • possibly less frequent on-chain transactions

CONS

  • Similarly needs to solve oracle input. It's not a problem in case of pairing BP-zkApp and self feeding the data
  • Trustlessness issue with factual operator config (BP can swap or abandon the identity)
  • Concurrency

4. HYBRID solution (???)

This is very vague idea. It would require daemon implementation that allows running zkApps as plugins, so we can remove dependency on the oracles. Basically, it would be one of the zkApp solutions listening to the network.

Concurrent usage of zkApps

This is an issue for all off-chain zkApps. Given that users interact with a zkApp with it's state hash, this state hash is different in case there is another user just before us. The optimistic concurrency solution fails here.

Consider this timeline:

  1. zkApp at State S1
  2. User A calls an app: delegate(AX, S1)
  3. User B calls an app in the same slot: delegate(BX, S1)
  4. User's B call is invalid as when his transaction arrives to the app it's state is S1(AX) = S2

Solutions

  1. Consider another concurrency strategies and data structures. Ring buffer for users' calls? API calls batching and parameters to roll them up?
  • Research needed
  1. Custom child tokens. In that case every delegator receives child token and we can manage concurrency by separating state changes from the parent zkApp. The danger here is a possibility of manipulation of child token balances. Potential hard forking changes needed to manage permissions for child tokens from parent token.
  • Prototype needed
  1. More roll-up like architecture. The drawback here is that batching mentioned above would be less transparent.

  2. Heavier client and UX handling of the concurrency issues (silent re-tries etc.)

Best guess effort level

FYI, Here is my best guess on level of effort to implement those:

  1. Protocol: Large, Daemon: Large
  2. Protocol: Small/None, Daemon: Small/None
  3. Protocol: Small/Medium, Daemon: Small/None
  4. Protocol: Medium: Daemon XLarge

Outro

The post got quite long... I will stop here. Please express your concerns about ideas above. We need more detailed requirements that will identify needed protocol changes. Also, from product point of view, it will shape final system which users interact with. CC: @jrwashburn @EmrePiconbello @es92 @teddyjfpender

@EmrePiconbello
Copy link

EmrePiconbello commented Feb 8, 2024

@michal0mina
I am fully supportive of an on-chain protocol-level solution. I understand it's the most complicated option, and given our limited block space, it's also quite challenging. However, here's the thing: to my knowledge, since 2017, no POS blockchain relies on validators for reward distribution. Anyone can claim or distribute rewards, or it's fully automated. Additionally, all rewards go directly to their intended recipients without the need for trust. As regulations continue to develop, and POS operations become more clearly defined, Mina doesn't fit the standard POS model in many cases. Custody is also a concern, with many regions reviewing it due to FTX collapse. We're not part of the foundation's delegation program because of these issues(While we can make it work, it requires a lot of effort from both us and the Foundation, which we deem unnecessary at this point, since the issue won't persist after the fork). While this isn't a major concern, it's difficult to justify when all POS chains function similarly, while Mina operates differently. A significant portion of the problem stems from custody, with many regions requiring e-money processing licenses (commonly used by fintech firms and challenger banks). This could lead to many validators being forced to exit Mina in the long run or face a 20% reduction in rewards due to taxes, or potentially face various charges since we are directly involved in signing transactions, making us liable under the law. That's why we need a trustless system where custody, distribution, calculation, etc., don't rely on validators.

From what I outlined above I could only support the first option while I didn't get the daemon part of that solution. I assume because no node actually hold data we are configurating the daemon to hold data for last 2 epochs but if you can elaborate more that would be very helpful.

@michal0mina
Copy link

@EmrePiconbello Thanks for the comment and legal insights. I incorporated them in the big comment

Re: the daemon solution -- this is it, but I mentioned those low level details as it's serious sub-system and long term maintenance, both for developers and node operators (as it is heavy system).

@jrwashburn
Copy link

jrwashburn commented Feb 12, 2024

Regarding the cons of a daemon solution: The daemon already has the entire delegation structure for the current and next epoch. The only thing to add (data-wise) would be the block producer fee.

Separately, having the rewards be claimable, vs. auto-incrementing, would be very useful from a tax perspective to give delegators the ability to control timing of receipt of rewards. While this isn't anticipated in the daemon solution @michal0mina has written above, it is something to consider. This feature would add significantly more complication due to a) wanting to consider the rewards in pos lottery, and b) providing an accumulator to keep unpaid rewards. I don't have a strong opinion re: adding this to the specification, but it should be considered given its prevalence in other protocols.

@michal0mina
Copy link

@jrwashburn Thanks

Re the daemon -- I will investigate what we have now. Yes, we have the delegation ledger, but I thought it is only on BP level.
Let me double check it.

About the logic that you describe:

  • it's basically choice between distributing delegation and accumulating delegation
  • I worked on a project that allowed delegators to specify when delegating which policy they prefer
  • I will add it as nice to have to the protocol solution

@jrwashburn
Copy link

Let me double check it.

The only place to get the staking ledgers (at least that I am aware of) is to export them from the daemon using mina ledger export [staking-epoch-ledger | next-epoch-ledger] (unless they are archived by someone who has done that - a la https://minaexplorer.com/staking-ledgers

@jrwashburn
Copy link

For the daemon solution, I think we need to consider:

New transactions:

  1. Register Block Producer Pool - need to be able to specify Pool Fee

  2. Unregister Block Producer - consider whether this is necessary, and if so, should it change the delegation of all delegates back to themselves? This transaction would allow all delegates to be aware that the pool is exiting block production (vs. simply not producing blocks.)

We could also consider an Update Fee transaction; however, I think it is best to unregister and re-register with the new fee, so that delegates have a clear choice to accept the new fee amount. A further complication to consider would be supporting update fee - but it only taking effect for delegates after a certain date. This all seems like more trouble than it is worth and I suggest the simpler register/unregister approach. I'm certain the community will come up with monitoring solutions to alert delegates of the unregistrations, and expect wallets would notify on unregister as well.

Another advantage of this approach would be that it could create an official registry of pools / those BP's offering to accept delegations. (Today, you can delegate to anyone, and we have seen instances of people setting up a pool fee of 100% to make it more clear that the are not a pool. This registry could be used to prevent delegating to people that do not want delegations.)

@michal0mina
Copy link

@jrwashburn re the delayed update fee, it would be reasonable to apply at least the same delay as for delegations (epoch+1). If there is need for signalling, it may be increased. Also, it's worth considering adding constrains (e.g. max 2x increase, or max 50% increase)

Lack of update mechanism would be painful for managing ecosystem of your users. Some sort of inheritance of the old delegators is surly helpful.

@Sventimir
Copy link

Sventimir commented Feb 13, 2024

I'm no expert in legal issues, but from purely technical point of view the simplest solution is to bake it into the protocol. Very simply, whenever the node constructing a block pays themselves a coinbase reward, it also scans the staking ledger in search of accounts delegating to them and immediately distributes the rewards among them. It may or may not add explicit transaction for each thus paid reward. While this is not necessary from the protocol's perspective (validators can compute those rewareds for themselves from the information already included in the block), it'll make it much easier for external software like indexers or Rosetta to figure out what's going on in the ledger (without those transactions they'd basically need to replicate the logic).

Putting this into a zkApp, while probably could be done without changing the protocol, will require someone to run the zkApp, so it's still up to a BP whether or not they run the zkApp before or after having produced a block. It does not eliminate the trust issue.

However, delaying the payment of these rewards on the protocol level complicates the system immensely. The unclaimed rewards must be kept somewhere so that total currency amount is predictable. Moreover, it forces us to implement an additional transaction type (claim rewards), which will have to be included in some future blocks. Also the information about who is entitled to claim how much must be stored somewhere. It cannot be computed from history unless we set a time limit after which unclaimed rewards are considered forefeit. On the other hand, implementing it in a zkApp seems relatively straightforward (all the information above will be stored in zkapp's state, probably even off-chain). So if we deem this feature really valuable, it's a strong argument for a zkapp solution.

@jrwashburn
Copy link

jrwashburn commented Feb 13, 2024

it would be reasonable to apply at least the same delay as for delegations (epoch+1). If there is need for signalling, it may be increased. Also, it's worth considering adding constrains (e.g. max 2x increase, or max 50% increase)

@michal0mina I agree these provisions mitigate the concern - and since monitoring by the delegator is necessary in every solution, as long as they would have a reasonable amount of time to re-delegate before the change, this overcomes my objections. I think Epoch +2 is necessary since the Epoch+1 staking ledger would already be baked.

Edited to add: In case it is not clear - I still think we want a registry with register/unregister transactions.

@0xstardust
Copy link

This thread seems very comprehensive. Interestingly, this decision has some downstream effects. At Kintsu we see a major overlap between this thread and the liquid staking RFC

In terms of making a scalable and secure liquid staking protocol, delegation rewards should be permissionless and trustless, and ideally, there is a simple way to claim rewards on-chain. Alternatively, there can be some runtime call made arbitrarily that allows rewards to be claimed.

@michal0mina
Copy link

Update:

  1. I removed issues related to concurrency. It's too close to implementation and likely solved by actions.
  2. Added important challenge/unknown with in-protocol solution: it may affect blockchain tip circuit

We also discussed deeper zkApp designed that would worked based on epoch's staking ledger.
@mitschabaude would you be able to summarize the design and potential issues/blockers?

cc: @es92

@mitschabaude
Copy link

We also discussed deeper zkApp designed that would worked based on epoch's staking ledger. @mitschabaude would you be able to summarize the design and potential issues/blockers?

In our discussion in March, we thought that we can use the stakingEpochData.ledger.hash precondition to prove the statement "I am allowed to withdraw staking reward" against the relevant ledger snapshot.

From the ledger hash, we should be able to prove both our account balance and our delegate at the time of the snapshot.

However, to prove how much we are allowed to withdraw depends on the total amount staked to the delegate. This is where we ended up getting blocked: The total stake per delegate is not something contained in the ledger or succinctly provable against it.

To explore this direction further, I would consider a sort of "zk coprocessor" to Mina which provides this missing information: A map from delegates -> total stake.
This map needs to be updated with every single transaction, so the coprocessor has to process all Mina transactions, and anchor them against the actual ledger, and update a recursive proof which tracks the delegate -> total stake map.

This coprocessor should probably be a shared piece of infrastructure because it is expensive to operate (needs to constantly churn out proofs).

Another option could be to update the delegate -> total stake map as part of the existing protocol, in the transaction snark. That would be a HF-requiring change, but otherwise I think a fairly non-intrusive protocol change.

It also might be the case that "historical preconditions" (which are on the protocol roadmap) are needed to get the relevant ledger snapshot (I don't know enough about staking/consensus to tell whether the existing stakingEpochData is the correct snapshot and whether it's realistic that all staking rewards get collected before stakingEpochData is updated to a different value)

@michal0mina
Copy link

@mitschabaude can zk cooprocessor be replaced with an oracle that serves appropriate ledger? Then the contract would be able to compute total delegated stake per delegate?

I am assuming here once per epoch computation and once per epoch claim. Probably with the zk cooprocessor it would be possible to jump in and out on demand and do not wait full epoch for accounting.

@mitschabaude
Copy link

@mitschabaude can zk cooprocessor be replaced with an oracle that serves appropriate ledger? Then the contract would be able to compute total delegated stake per delegate?

The problem is that a computation based on the current ledger would have to process every single account in the ledger and fully re-derive its hash. Otherwise, there is no way to prove that your calculation includes all accounts that are delegating to a particular delegate.

I think processing the entire ledger in a zk circuit, every epoch, might be prohibitively expensive. Starting from a known correct delegate->total_stake map and updating it by using all new transactions applied to the ledger in between might be cheaper.

@michal0mina
Copy link

@mitschabaude is there any middle ground? Can we use the ledger and hash as arguments and have outside tool to show these are complete and match each other?

@mitschabaude
Copy link

mitschabaude commented Jul 29, 2024

@mitschabaude is there any middle ground? Can we use the ledger and hash as arguments and have outside tool to show these are complete and match each other?

that's kinda where my zk coprocessor idea was going at

alternatively you could have a trusted source that signs the data that the zkapp needs. (that source would have to be very trusted though because it determines who gets paid staking rewards)

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.

10 participants