Skip to content

Commit

Permalink
Merge pull request #171 from osmosis-labs/slashing-docs-update
Browse files Browse the repository at this point in the history
Slashing docs update
  • Loading branch information
JakeHartnell authored Dec 6, 2023
2 parents 8e81e70 + f6f3569 commit 4402153
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 21 deletions.
2 changes: 2 additions & 0 deletions contracts/provider/external-staking/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ impl Stake {

/// Slashes all the entries in `pending_unbonds`, returning total slashed amount.
pub fn slash_pending(&mut self, info: &BlockInfo, slash_ratio: Decimal) -> Uint128 {
// TODO: Only slash undelegations that started after the misbehaviour's time. This is not
// possible right now, because we don't have access to the misbehaviour's time. (#177)
self.pending_unbonds
.iter_mut()
.filter(|pending| pending.release_at > info.time)
Expand Down
1 change: 1 addition & 0 deletions contracts/provider/native-staking-proxy/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ impl NativeStakingProxyContract<'_> {
}

// Build undelegate messages
// FIXME: Use an "immediate unbonding" message for undelegation
let mut undelegate_msgs = vec![];
for (validator, burn_amount) in burns {
let undelegate_msg = StakingMsg::Undelegate {
Expand Down
2 changes: 0 additions & 2 deletions contracts/provider/native-staking/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ impl NativeStakingContract<'_> {
}
for validator in jailed {
// Slash the validator (if bonded)
// TODO: Slash with a different slash ratio! (downtime / offline slash ratio)
let slash_msg =
self.handle_slashing(&mut deps, &cfg, validator, SlashingReason::Offline)?;
if let Some(msg) = slash_msg {
Expand Down Expand Up @@ -157,7 +156,6 @@ impl NativeStakingContract<'_> {

if delegation.is_zero() {
// Maintenance: Remove delegator from map in passing
// TODO: Remove zero amount delegations from delegators map periodically
self.delegators.remove(deps.storage, (validator, owner));
continue;
}
Expand Down
111 changes: 92 additions & 19 deletions docs/ibc/Slashing.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Slashing Evidence Handling

Note: Slashing will not be part of the MVP rollout, and first implemented in V1. However, we define
**Note**: Slashing will not be part of the MVP rollout, and first implemented in V1. However, we define
proper slashing mechanics here.

**Note 2**: As of V1, we will assume that the Consumer chain is not Byzantine, and therefore
we will not implement a mechanism to verify slashing evidence. This will be implemented in V2.

## General Architecture

We are worried about a Byzantine consumer chain slashing arbitrary validators on the provider
Expand All @@ -15,10 +18,28 @@ Rather than submit IBC packets for Slashing, we require submission of the duplic
signatures from the Tendermint headers on the Consumer chain. These cannot be forged
unless the private key is compromised.

As of V2, the external staker must have a method to allow submitting such evidence of
As of V2, the external staker (on the Provider side) must have a method to allow submitting such evidence of
double-signing which can be verified and immediately slash all delegators of that
validator.

This can be done by following the approach and implementation of InterChain Security (ICS) for
[slashing](https://cosmos.github.io/interchain-security/adrs/adr-013-equivocation-slashing). At the time of writing,
there's already an implementation of ICS misbehaviour handling on the [Hermes relayer](https://github.com/informalsystems/hermes).
We can therefore use that as a reference.

The general idea is that the Relayer will submit the misbehaviour evidence to the Provider chain,
which will then slash the associated delegators. The evidence will be verified by the
Provider chain, and only valid evidence will be accepted and processed.

The submission mechanism is straightforward: the Relayer submits the slashing evidence from the
Consumer to the Provider, and broadcasts it as or as part of a blockchain transaction. So, the Provider
will need to have a slashing module that monitors the chain for a specific "Slashing evidence"
transaction type. The slashing evidence can then be submitted to a smart contract (as a sudo message by example) for
verification and processing.

Please note that the actual slashing implementation will not change. Only the slashing evidence
handling, submission and verification will need to be implemented as part of V2.

## Detecting Byzantine Chains

The IBC light clients have a
Expand Down Expand Up @@ -67,6 +88,9 @@ evidence. Or we just accept any age and just use the age of the evidence to deci
of the two votes may be wildly different, and they really shouldn't have trusted this
cheating validator in the first place.

**Note**: Here we can again refer to the ICS specs / impl for reference:
https://cosmos.github.io/interchain-security/adrs/adr-005-cryptographic-equivocation-verification

## Trust Assumptions

For the V1 implementation, we will assume not only that the Tendermint headers are valid and can be trusted, but
Expand All @@ -75,6 +99,8 @@ having to implement a different / independent communication channel for misbehav
Once that mechanism is established and implemented, by example as part of ICS, we can revisit this and adapt our implementation
to receive and verify misbehaviour evidence from the Consumer chain on the Provider.

**Note**: As of this update (28/11/23) this mechanism has already been implemented as part of the Hermes relayer.

So, this is concerned with a malicious validator on the Consumer chain, double-signing to slash associated delegators
on the Provider chain.

Expand All @@ -83,15 +109,19 @@ A user delegating to a malicious validator and then getting slashed is part of t
the delegator is getting staking rewards.
As a mitigating factor, the amount of slashing for misbehaviour is defined by the slashing ratio.

Another possibility is, a malicious validator on the Consumer double signing for profit, and trying to **avoid** being slashed.
Another possibility is, a malicious validator on the Consumer double sign intentionally, and try to **avoid** being slashed.
He could, by example, allocate all or most of his funds through cross-delegators on the Provider, and then tamper with
the validator set updates, so that his public key, or associated block height and times, are invalid. This would prevent the
Provider from slashing him, as the **provided evidence for misbehaviour would fail to verify**.
This last scenario is only possible if the entire Consumer chain goes Byzantine, and included here just for completeness.
It shows that the trust assumptions extend beyond the misbehaviour's evidence, and should include
the validator set updates as well. Along with the Consumer chain itself.

This indicates that, barring Byzantine Consumer chains, it makes sense to re-utilize the same infrastructure and mechanisms
**Note**: As of this update (28/11/23) AFAIK this scenario is already being handled by the Hermes relayer.
The misbehaviour evidence is verified against the validator set at the time of the misbehaviour, and the
validator public key, which is needed for verification, is included in the evidence itself.

All this indicates that, barring Byzantine Consumer chains, it makes sense to re-utilize the same infrastructure and mechanisms
that are used for communication between the Provider and the Consumer, for the specific case of slashing processing.
Both, slashing evidence handling and submission, and validator set updates, share similar trust assumptions and concerns.
They can and must then be part of the same security model.
Expand All @@ -101,6 +131,15 @@ separate from the rest of the Mesh Security infrastructure (in their own smart c
then we should make sure that slashing evidence handling and submission is done by the same entity that is responsible for
validator set updates. So that they can be audited together, and the same trust assumptions apply to both.

**Note**: As of this update (28/11/23) this is already the case. The Hermes relayer is responsible for both, validator public key
and misbehaviour evidence handling and submission.

**Note 2**: Another possibility in this scenario is a malicious validator on the Consumer relaying and submitting false / forged misbehaviour evidence to the Provider,
through the Relayer. That is, tricking the Relayer into submitting false slashing evidence to the Provider, in order to slash associated Provider's delegators.
This is in principle possible, and called a "Nothing at Stake" attack (since the validator has in principle nothing to lose on the Consumer chain when doing this).
To prevent against this, the Relayer must broadcast / replay the slashing evidence it gets on the Consumer chain. This way, if a validator forged
the evidenced, it will be slashed on the Consumer side anyway; so that it's no longer a "nothing at stake" scenario.

## Slashing Handling

As mentioned, for V1, and for simplicity reasons, we will implement a slashing mechanism as part of the existing infrastructure,
Expand Down Expand Up @@ -132,10 +171,17 @@ The `vault` contract will need to be able to handle this scenario.
In the case of a slashed cross-delegator that doesn't have enough liquidity on the vault, it will need to unbond the funds
from the Provider chain first, and then burn them to execute the slashing.

**Note**: As of this update (28/11/23) this is the current setup. Instead of burning the funds, they are unbonded from the Provider chain,
and kept in the Native staking contract. An accounting trick is being done, to avoid the need to burn the funds
and to discount the slashing amount from the total collateral of the delegator.

A kind of "immediate unbonding" mechanism (and associated permission) could be needed for this.
Alternatively, the vault can unbond the funds from the Provider chain and wait for the unbonding period to expire in order to slash them.
This will be effectively the same, as during unbonding those funds are both, blocked from withdrawal, and not providing rewards.

**Note**: This is currently (V1) being done using a normal unbonding period, which is not ideal, as the funds are being doubly discounted
by the slashing amount during the unbonding period. This will be fixed in V2, by implementing "immediate unbonding" on the Provider chain.

Another option would be for the vault to delegate the slashing in these cases to the blockchain itself.
That is, by using and interacting with the Provider chain's Staking / Slashing module.
This may be complicated to implement, as the slashing evidence and origin are effectively from another chain.
Expand All @@ -149,6 +195,8 @@ From the point of view of slashing, both local and cross slashing events must be
The only difference being that local slashing events don't need to be verified. But local slashing events
must be processed, and its effects on collateral must be updated in the vault for each of the affected delegators.

**Note**: This is not yet implemented as of V1. It will be implemented as part of V2.

## Slashing Propagation

Slashing affects the amount of total collateral that every affected delegator has on the vault (or natively staked).
Expand All @@ -158,7 +206,9 @@ After validating the slashing evidence (in the case of cross-slashing) and execu
that is, discounting the slashing amount from the total collateral each affected delegator has, the associated invariants
must be checked and adjusted if needed.
In the case of a broken invariant, a rebalance (unbonding of the now lacking or insufficient funds) must be done to restore it.
This must be checked and rebalanced if needed over all the chains that the affected delegator has funds on.
This must be checked and rebalanced if needed, over all the chains that the affected delegator has funds on.

**Note**: Both Slashing accounting and Slashing propagation accounting have been implemented as part of V1.

## Collateral Unbonding and Slashing Propagation Examples

Expand Down Expand Up @@ -338,25 +388,48 @@ to the aggregated slash ratios of all the lien holders.
- `new slashable amount: 78.125 * 0.1 + 68.125 * 0.5 + 58.125 * 0.5 + 78.125 * 0.5 = 7.8125 + 34.0625 + 29.0625 + 39.0625 = 110.0` (recalculated)
- `new free collateral: 110 - max(110, 78.125) = 110 - 110 = 0` (invariants restored)

**TODO**
## Slashing Process Summary

We can see that there are two processes potentially at play, at the smart contracts level, during slashing:

1) Slashing accounting itself, which is done by the `vault` contract, over the users associated with
the slashed validator, on the corresponding lien holder.
Slashing accounting is also adjusted "in passing" (while forwarding the slashing to the `vault` contract),
in both the `virtual-staking` contract on the Consumer, and the `external-staking` contract on the Provider.

2) Slashing propagation, which is done by the `vault` contract over the other lien holders associated
with the delegator or delegators that are associated in turn to the slashed validator. This happens
in case the slashed collateral in point 1), is now less than the new max lien, or the new slashable amount.

This process starts at the `vault` contract, and is propagated to the `virtual-staking` contract on the Consumer.
It can also involve the native staking contracts on the Provider, if the slashed collateral is natively staked.
This in turn involves a kind of on-chain **burn** mechanism for the slashed funds; which is currently being done
by unbonding them, and discounting them from the total collateral of the delegator.

**Note**: This unbonding mechanism is already "immediate unbonding" on the Consumer side, and will be implemented
as such on the Provider side as well, as part of V2 (or earlier).

### Slashing Process Summary
Depending on which is the reason for the broken invariant (either max lien greater than collateral, or total slashable amount greater than collateral),
slashing propagation will be done differently:
Either, by **adjusting the offending liens** to be below the collateral. Or, by **proportionally adjusting all the liens**, so that the sum of
the resulting sum of slashable amounts is below the collateral.

We can see that there are two processes potentially at play during slashing:
### Native vs. Cross Slashing

1) Slashing itself, which is done by the `vault` contract over the users associated with the slashed validator,
on the corresponding lien holder.
2) Slashing propagation, which is done by the `vault` contract over the other lien holders associated with the delegator,
in case the slashed collateral is now less than the new max lien or the new slashable amount.
Native vs. Cross Slashing processing and effects are similar, and are being implemented in the same way.
The main difference is that as of V1, we currently lack a **native** `x/meshsecurity` module (that is, for the Provider blockchain),
and therefore cannot do immediate unbonding. This will be implemented as part of V2.

Depending on which of the two is the reason for the broken invariant, slashing propagation will be done differently:
Either by adjusting the liens to be below the collateral, or by proportionally adjusting the liens, so that the sum of
the resulting slashable amounts is below the collateral.
### Effects of Validator Tombstoning During Slashing

### Native vs. Cross Slashing Process Details
Validator tombstoning, when or as a consequence of double signing, permanently removes the validator from the validator set.
Validator jailing, when or as a consequence of offline detection, also temporarily removes the validator from the active validator set.

**TODO**
Both events also lead to slashing, with different slash ratios for each misbehaviour. Only active validators
can be slashed, and this is a check that is currently done as part of the slashing process.

### Effects of Validator Tomstoning During Slashing
This means that if a validator is tombstoned or jailed during slashing, it has to be slashed first, and then tombstoned or jailed.
This is because the slashing process is done over the active validator set, and the validator must be active at the time of slashing.

**TODO**
This is currently being implemented this way, for cross-validators, in both the `virtual-staking` contract on the Consumer,
and the `external-staking` contract on the Provider.

0 comments on commit 4402153

Please sign in to comment.