Cubic slashing
Namada implements a slashing scheme that is called cubic slashing: the amount of a slash is proportional to the cube of the voting power committing infractions within a particular interval. This is designed to make it riskier to operate larger or similarly configured validators, and thus the scheme encourages network resilience.
When a slash is detected:
- Using the height of the infraction, calculate the epoch at the unbonding length relative to the current epoch. This is the final epoch before the stake that was used to commit the infraction can be fully unbonded and withdrawn. The slash is enqueued to be processed in this final epoch to allow for sufficient time to detect any other validator misbehaviors while still processing the slash before the infraction stake could be unbonded and withdrawn.
- Jail the misbehaving validator, effective at the beginning of the next epoch. While the validator is jailed, it is removed from the validator set. Note that this is the only instance in our proof-of-stake model wherein the validator set is updated without waiting for the pipeline offset.
- Prevent the delegators to this validator from altering their delegations in any way until the enqueued slash is processed.
At the end of each epoch, for each slash enqueued to be processed for the end of the epoch:
- Collect all known infractions committed within a range of [-
window_width
, +window_width
] epochs around the infraction in question. By default,window_width
= 1. - Sum the fractional voting powers (relative to the total PoS voting power) of the misbehaving validator for each of the collected nearby infractions.
- The final slash rate for the slash in question is then dependent on this sum. Using as the nominal slash rate and to indicate voting power, the slash rate is expressed as:
Or, in pseudocode:
#![allow(unused)] fn main() { // Infraction type, where inner field is the slash rate for the type enum Infraction { DuplicateVote(Decimal), LightClientAttack(Decimal) } // Generic validator with an address and voting power struct Validator { address: Vec<u8>, voting_power: u64, } // Generic slash object with the misbehaving validator and infraction type struct Slash { validator: Validator, infraction_type: Infraction, } // Calculate the cubic slash rate for a slash in the current epoch fn calculate_cubic_slash_rate( current_epoch: u64, nominal_slash_rate: Decimal, cubic_window_width: u64, slashes: Map<u64, Vec<Slash>>, total_voting_power: u64 ) -> Decimal { let mut vp_frac_sum = Decimal::ZERO; let start_epoch = current_epoch - cubic_window_width; let end_epoch = current_epoch + cubic_window_width; for epoch in start_epoch..=end_epoch { let cur_slashes = slashes.get(epoch); let vp_frac_this_epoch = cur_slashes.iter.fold(0, |sum, Slash{validator, _}| { sum + validator.voting_power / total_voting_power} ); vp_frac_sum += vp_frac_this_epoch; } let rate = cmp::min( Decimal::ONE, cmp::max( slash.infraction_type.0, 9 * vp_frac_sum * vp_frac_sum, ), ); rate } }
As a function, it can be drawn as (assuming ):
- Set the slash rate on the now "finalised" slash in storage.
- Update the misbehaving validators' stored voting powers appropriately.
- Delegations to the validator can now be redelegated / start unbonding / etc.
Note: The voting power associated with a slash is the voting power of the validator when they violated the protocol. This does mean that these voting powers may not sum to 1, but this method should still be close to the desired incentives and cannot really be changed without making the system easier to game.
A jailed validator can later submit a transaction to unjail themselves after a configurable period. When the transaction is applied and accepted, the validator updates its state to "candidate" and is added back to the appropriate validator set (depending on its new voting power) starting at the pipeline offset relative to the epoch in which the unjailing transaction was submitted.
At present, funds slashed are sent to the governance treasury.
Slashes
Slashes should lead to punishment for delegators who were contributing voting power to the validator at the height of the infraction, as if the delegations were iterated over and slashed individually.
This can be implemented as a negative inflation rate for a particular block.