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

Questions about slashing in vault #32

Open
Tracked by #85
hashedone opened this issue May 23, 2023 · 1 comment
Open
Tracked by #85

Questions about slashing in vault #32

hashedone opened this issue May 23, 2023 · 1 comment
Labels
question Further information is requested
Milestone

Comments

@hashedone
Copy link
Collaborator

As I understand, slashing is:

The staking contract (the creditor) is getting slashed for x% (accumulated slashing can never exceed max_slash%). Then he has to forward information to the vault contract that he got slashed for x%, and all the users who staked collateral on this contract should have it slashed (the collateral) by x% of the amount taken on this creditor (not x% of the entire collateral). The basics seemed very legit, but when I wanted to clarify it, I needed help to figure out the proper implementation.

Slashing cross staker

It is obvious what happens with slashed tokens in case of slashing a local staker - after slashing vault would never get the slashed token back. The problem arises when talking about slashing cross-staker contracts, as we never send tokens to this contract, so nothing gets "burned".

My initial idea was that in such a case, we could just burn tokens on the vault contract. So let's say user1 has a collateral of 300 atoms, and he staked 100 atoms on cross1 staker, with max_slash of 5%. That immediately means that 5 atoms are removed from user1 collateral and his stake, so he has now collateral of 295 atoms and 95 atoms staken on cross1. Additionally, we burn 5 atoms from the vault contract.

However, I realized that there are possible cases when, with this approach, we can have not enough tokens to burn, or by burning them, we will not be able to unbound tokens for the other user who didn't get slashed (he didn't stake on cross1). The trivial case here is: user1 is the only user of the protocol, and he staken all 300 atoms on local staking (transferring tokens) with 5% max slash (all is working, 5% * 300 + 5% * 100 = 15 + 5 = 20 < 300).

My other take is - we burn tokens as I suggested, but only up to the number of tokens not staken on local staking (so we burn tokens the particular user bond before). If there are not enough tokens, then a user goes into dept - we store the amount of debt on him. Whenever the user is in debt and he is providing tokens to the vault by either bonding new tokens or getting tokens released from local staking (or possibly other ways I don't see or are not yet defined), those tokens are immediately burned, and debt is reduced.

The question about this approach is - is there a valid case when a user would never be willing to unstake tokens from a local staker, and how much problem is that? Possibly it could be solved by refusing to perform some actions in the protocol when the user is in debt (contracts could query the vault if a user is legitimate).

Yet another idea would be to provide a way to forcefully unstake tokens that needed to be burned from the local staker by vault. This would be simple; not sure if reasonable.

Accumulating slashing

I don't know how slashing accumulates when the user stakes and releases tokens.

Use case: user1 stakes 100 atoms on local staker, max slash 10%. Local staker gets slashed for 3% - user1 has now staked 97 atoms on local staker. He can be staked for 7 atoms more in the future.

Now user1 stakes another 100 atoms, so he has staken 197 atoms. How much can he be slashed for in the future? I kind of think it is 17 atoms, as tokens he is just stashing was not yet been slashed, so 10% of them still can be slashed. But then I get info that the local staker got slashed for another 1% - how many tokens are removed from user1 stake (and collateral)? I have really no idea.

And even more mysterious to me is - when the user gets stake released - how is the slashing change (as some tokens "batch" can be slashed for the different mounts).

My take here is that I overthink this, and this 10% means that the staker can be slashed up to this amount for his entire existence, and after that, he can only be jailed, so any slashing action reduces the max_slash for the given staker. That makes sense and is easy, the question is - is there ever a case that stakers gets his max_slash back and how is it handled

Who is in charge of iteration

This is a minor implementation-wise question but sure to be considered API-wise.

When staker gets slashed, staking and collateral info for all users who staken tokens to him is to be updated. But that means that either the vault contract has to go through all the users who staken on the contract, or staking contract should do that to slash all the users.

It looks like it could be a long iteration, and I think it could be avoided by only sending the info that staker got slashed, and not updating all the users - only storing information on how many tokens from the staker got slashed, and performing the collateral/staking reduction when the user gets tokens released from the staker. I am thinking about the variation of cw2222 (kind of reversed, but I think I have an idea how to solve that) delaying the calculation of slashing to the point of tokens release. This kind of makes the first problem even more complex (as we don't know whose tokens to burn when slashing cross-staker), but if we burn those tokens on their release, it feels like all is holding. Honestly, it can be an easier problem that looks as if I didn't spend much time on this particular one, but got a short think, however wondering if there are already some ideas about that.

@hashedone hashedone added the question Further information is requested label May 23, 2023
@ethanfrey
Copy link
Collaborator

Very good questions. I don't have clear answers for any of them, and happy to work with others to clarify and specify.

For MVP we need a spec of how slashing should work. Implementation comes in the next milestone (v1)

The first two may be made easier understanding that the collateral recorded in vault must be <= vault balance + locally staked. That is, the vault may manage more funds than belong to the users. Ideally it would unstake these and eventually burn them or such, but it is not required to keep these values strictly equal.

The question on iteration is a good one, and the biggest implementation issue to implementing slashing (rather than design issue). I'm not sure how to handle this. I mean, updating one contract (like the Juno cross-staking contract) is doable in some lazy way. The bigger issue is if validator X gets slashed on Juno, causing some delegators (D1...Dn) to be slashed on Osmosis, then we need to check with the vault if they have any leins outside their new balance. Not only locally, but also, say, on Stargaze.

Iteration is O(N) expensive.
I was thinking of some way of caching with a multi-dimensional Co-occurrence matrix mapping overlapping stake between chain A, chain B, and local stake validators, so we can at least update the delegations quickly and lazily calculate the user's shares (when they next try to deposit, withdraw, etc).

This may be doable with a 2-d array (100 validator x 100 validators). Storing it for say 5 remote chains and a local chains would mean 100^5 possible entries. Of course, we can do this all "sparse" and not write all data, but not sure this is the best bet. So far, this is my brainstorming on the issue and happy to dig in more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants