From 9cb7f3c78281138f731167f0b40a9d77116626c6 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Tue, 3 Jun 2025 19:38:07 +0300 Subject: [PATCH] replacing the average claps in session with median to determine a harmful authority Signed-off-by: Uncle Stinky --- pallets/slow-clap/Cargo.toml | 2 +- pallets/slow-clap/src/lib.rs | 45 +++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index 40cb7d8..ef217c2 100644 --- a/pallets/slow-clap/Cargo.toml +++ b/pallets/slow-clap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-slow-clap" -version = "0.3.17" +version = "0.3.18" description = "Applause protocol for the EVM bridge" license.workspace = true authors.workspace = true diff --git a/pallets/slow-clap/src/lib.rs b/pallets/slow-clap/src/lib.rs index 8fea071..6c5e4dc 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -858,9 +858,9 @@ impl Pallet { fn is_good_actor( authority_index: usize, session_index: SessionIndex, - average_claps: u32, + median_claps: u32, ) -> bool { - if average_claps == 0 { + if median_claps == 0 { return true; } @@ -869,10 +869,10 @@ impl Pallet { .or_default() .claps; - let authority_deviation = if number_of_claps < average_claps { - Perbill::from_rational(average_claps - number_of_claps, average_claps) + let authority_deviation = if number_of_claps < median_claps { + Perbill::from_rational(median_claps - number_of_claps, median_claps) } else { - Perbill::from_rational(number_of_claps - average_claps, average_claps) + Perbill::from_rational(number_of_claps - median_claps, median_claps) }; authority_deviation < Perbill::from_percent(T::OffenceThreshold::get()) } @@ -886,6 +886,28 @@ impl Pallet { } } + fn calculate_median_claps(session_index: &SessionIndex) -> u32 { + let mut claps_in_session = ClapsInSession::::get(session_index) + .values() + .filter_map(|value| (!value.disabled).then(|| value.claps)) + .collect::>(); + + if claps_in_session.is_empty() { + return 0; + } + + claps_in_session.sort(); + let number_of_claps = claps_in_session.len(); + + if number_of_claps % 2 == 0 { + let mid_left = claps_in_session[number_of_claps / 2 - 1]; + let mid_right = claps_in_session[number_of_claps / 2]; + (mid_left + mid_right) / 2 + } else { + claps_in_session[number_of_claps / 2] + } + } + #[cfg(test)] fn set_test_authorities(authorities: Vec) { let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities) @@ -931,21 +953,12 @@ impl OneSessionHandler for Pallet { let validators = T::ValidatorSet::validators(); let authorities = Authorities::::get(); - let (sum_claps, total_claps) = ClapsInSession::::get(&session_index) - .iter() - .filter(|(_, value)| !value.disabled) - .fold((0u32, 0u32), |(sum, total), (_, value)| - (sum.saturating_add(value.claps), total.saturating_add(1)) - ); - - let average_claps = sum_claps - .checked_div(total_claps) - .unwrap_or_default(); + let median_claps = Self::calculate_median_claps(&session_index); let offenders = validators .into_iter() .enumerate() - .filter(|(index, _)| !Self::is_good_actor(*index, session_index, average_claps)) + .filter(|(index, _)| !Self::is_good_actor(*index, session_index, median_claps)) .filter_map(|(_, id)| { >::IdentificationOf::convert( id.clone(),