use crate::*;
use frame_support::defensive;
pub trait DisablingStrategy<T: Config> {
fn decision(
offender_stash: &T::ValidatorId,
offender_slash_severity: OffenceSeverity,
currently_disabled: &Vec<(u32, OffenceSeverity)>,
) -> DisablingDecision;
}
#[derive(Debug)]
pub struct DisablingDecision {
pub disable: Option<u32>,
pub reenable: Option<u32>,
}
impl<T: Config> DisablingStrategy<T> for () {
fn decision(
_offender_stash: &T::ValidatorId,
_offender_slash_severity: OffenceSeverity,
_currently_disabled: &Vec<(u32, OffenceSeverity)>,
) -> DisablingDecision {
DisablingDecision { disable: None, reenable: None }
}
}
fn factor_based_disable_limit(validators_len: usize, disabling_limit_factor: usize) -> usize {
validators_len
.saturating_sub(1)
.checked_div(disabling_limit_factor)
.unwrap_or_else(|| {
defensive!("DISABLING_LIMIT_FACTOR should not be 0");
0
})
}
pub struct UpToLimitDisablingStrategy<const DISABLING_LIMIT_FACTOR: usize = 3>;
impl<const DISABLING_LIMIT_FACTOR: usize> UpToLimitDisablingStrategy<DISABLING_LIMIT_FACTOR> {
pub fn disable_limit(validators_len: usize) -> usize {
factor_based_disable_limit(validators_len, DISABLING_LIMIT_FACTOR)
}
}
impl<T: Config, const DISABLING_LIMIT_FACTOR: usize> DisablingStrategy<T>
for UpToLimitDisablingStrategy<DISABLING_LIMIT_FACTOR>
{
fn decision(
offender_stash: &T::ValidatorId,
_offender_slash_severity: OffenceSeverity,
currently_disabled: &Vec<(u32, OffenceSeverity)>,
) -> DisablingDecision {
let active_set = Validators::<T>::get();
if currently_disabled.len() >= Self::disable_limit(active_set.len()) {
log!(
debug,
"Won't disable: reached disabling limit {:?}",
Self::disable_limit(active_set.len())
);
return DisablingDecision { disable: None, reenable: None };
}
let offender_idx = if let Some(idx) = active_set.iter().position(|i| i == offender_stash) {
idx as u32
} else {
log!(debug, "Won't disable: offender not in active set",);
return DisablingDecision { disable: None, reenable: None };
};
log!(debug, "Will disable {:?}", offender_idx);
DisablingDecision { disable: Some(offender_idx), reenable: None }
}
}
pub struct UpToLimitWithReEnablingDisablingStrategy<const DISABLING_LIMIT_FACTOR: usize = 3>;
impl<const DISABLING_LIMIT_FACTOR: usize>
UpToLimitWithReEnablingDisablingStrategy<DISABLING_LIMIT_FACTOR>
{
pub fn disable_limit(validators_len: usize) -> usize {
factor_based_disable_limit(validators_len, DISABLING_LIMIT_FACTOR)
}
}
impl<T: Config, const DISABLING_LIMIT_FACTOR: usize> DisablingStrategy<T>
for UpToLimitWithReEnablingDisablingStrategy<DISABLING_LIMIT_FACTOR>
{
fn decision(
offender_stash: &T::ValidatorId,
offender_slash_severity: OffenceSeverity,
currently_disabled: &Vec<(u32, OffenceSeverity)>,
) -> DisablingDecision {
let active_set = Validators::<T>::get();
let offender_idx = if let Some(idx) = active_set.iter().position(|i| i == offender_stash) {
idx as u32
} else {
log!(debug, "Won't disable: offender not in active set",);
return DisablingDecision { disable: None, reenable: None };
};
if let Some((_, old_severity)) =
currently_disabled.iter().find(|(idx, _)| *idx == offender_idx)
{
if offender_slash_severity > *old_severity {
log!(debug, "Offender already disabled but with lower severity, will disable again to refresh severity of {:?}", offender_idx);
return DisablingDecision { disable: Some(offender_idx), reenable: None };
} else {
log!(debug, "Offender already disabled with higher or equal severity");
return DisablingDecision { disable: None, reenable: None };
}
}
if currently_disabled.len() >= Self::disable_limit(active_set.len()) {
log!(
debug,
"Reached disabling limit {:?}, checking for re-enabling",
Self::disable_limit(active_set.len())
);
if let Some((smallest_idx, _)) = currently_disabled
.iter()
.filter(|(_, severity)| *severity <= offender_slash_severity)
.min_by_key(|(_, severity)| *severity)
{
log!(debug, "Will disable {:?} and re-enable {:?}", offender_idx, smallest_idx);
return DisablingDecision {
disable: Some(offender_idx),
reenable: Some(*smallest_idx),
};
} else {
log!(debug, "No smaller offender found to re-enable");
return DisablingDecision { disable: None, reenable: None };
}
} else {
log!(debug, "Will disable {:?}", offender_idx);
return DisablingDecision { disable: Some(offender_idx), reenable: None };
}
}
}