fix calculation of median, include special case when there're no claps

Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
Uncle Stinky 2025-07-31 13:56:05 +03:00
parent f7b1b75d5a
commit c4b16805f7
Signed by: st1nky
GPG Key ID: 016064BD97603B40
3 changed files with 169 additions and 9 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-slow-clap" name = "ghost-slow-clap"
version = "0.3.35" version = "0.3.36"
description = "Applause protocol for the EVM bridge" description = "Applause protocol for the EVM bridge"
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -90,7 +90,7 @@ pub struct Clap<AccountId, NetworkId, Balance> {
pub amount: Balance, pub amount: Balance,
} }
#[derive(Default, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] #[derive(Default, Clone, Copy, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct SessionAuthorityInfo { pub struct SessionAuthorityInfo {
pub claps: u32, pub claps: u32,
pub disabled: bool, pub disabled: bool,
@ -967,10 +967,18 @@ impl<T: Config> Pallet<T> {
.ok_or(OffchainErr::ErrorInEvmResponse)?) .ok_or(OffchainErr::ErrorInEvmResponse)?)
} }
fn calculate_median_claps(session_index: &SessionIndex) -> u32 { fn calculate_median_claps(
let mut claps_in_session = ClapsInSession::<T>::get(session_index) actual_claps_in_session: &BTreeMap<AuthIndex, SessionAuthorityInfo>,
.values() authorities_len: usize,
.filter_map(|value| (!value.disabled).then(|| value.claps)) ) -> u32 {
let mut claps_in_session = (0..authorities_len)
.filter_map(|index| {
let clap_info = actual_claps_in_session
.get(&(index as u32))
.copied()
.unwrap_or_default();
(!clap_info.disabled).then(|| clap_info.claps)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if claps_in_session.is_empty() { if claps_in_session.is_empty() {
@ -1083,9 +1091,10 @@ impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
fn on_before_session_ending() { fn on_before_session_ending() {
let session_index = T::ValidatorSet::session_index(); let session_index = T::ValidatorSet::session_index();
let validators = T::ValidatorSet::validators(); let validators = T::ValidatorSet::validators();
let authorities = Authorities::<T>::get(&session_index); let authorities_len = Authorities::<T>::get(&session_index).len();
let actual_claps_in_session = ClapsInSession::<T>::get(&session_index);
let median_claps = Self::calculate_median_claps(&session_index); let median_claps = Self::calculate_median_claps(&actual_claps_in_session, authorities_len);
let offenders = validators let offenders = validators
.into_iter() .into_iter()
@ -1105,7 +1114,7 @@ impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
throttling: offenders.clone(), throttling: offenders.clone(),
}); });
let validator_set_count = authorities.len() as u32; let validator_set_count = authorities_len as u32;
let offence = ThrottlingOffence { let offence = ThrottlingOffence {
session_index, session_index,
validator_set_count, validator_set_count,

View File

@ -119,6 +119,157 @@ fn test_throttling_slash_function() {
); );
} }
#[test]
fn test_median_calculations_are_correct() {
new_test_ext().execute_with(|| {
let data = BTreeMap::from([
(
0u32,
SessionAuthorityInfo {
claps: 0,
disabled: true,
},
),
(
3u32,
SessionAuthorityInfo {
claps: 1,
disabled: false,
},
),
]);
assert_eq!(SlowClap::calculate_median_claps(&data, 4), 0);
let data = BTreeMap::from([
(
0u32,
SessionAuthorityInfo {
claps: 0,
disabled: false,
},
),
(
1u32,
SessionAuthorityInfo {
claps: 69,
disabled: false,
},
),
(
2u32,
SessionAuthorityInfo {
claps: 69,
disabled: false,
},
),
(
3u32,
SessionAuthorityInfo {
claps: 420,
disabled: false,
},
),
]);
assert_eq!(SlowClap::calculate_median_claps(&data, 4), 69);
let data = BTreeMap::from([
(
0u32,
SessionAuthorityInfo {
claps: 31,
disabled: false,
},
),
(
1u32,
SessionAuthorityInfo {
claps: 420,
disabled: true,
},
),
(
2u32,
SessionAuthorityInfo {
claps: 69,
disabled: false,
},
),
(
3u32,
SessionAuthorityInfo {
claps: 156,
disabled: true,
},
),
]);
assert_eq!(SlowClap::calculate_median_claps(&data, 4), 50);
let data = BTreeMap::from([
(
0u32,
SessionAuthorityInfo {
claps: 0,
disabled: true,
},
),
(
1u32,
SessionAuthorityInfo {
claps: 420,
disabled: false,
},
),
(
2u32,
SessionAuthorityInfo {
claps: 0,
disabled: true,
},
),
(
3u32,
SessionAuthorityInfo {
claps: 0,
disabled: false,
},
),
]);
assert_eq!(SlowClap::calculate_median_claps(&data, 4), 210);
let data = BTreeMap::from([
(
0u32,
SessionAuthorityInfo {
claps: 0,
disabled: false,
},
),
(
1u32,
SessionAuthorityInfo {
claps: 420,
disabled: true,
},
),
(
2u32,
SessionAuthorityInfo {
claps: 69,
disabled: true,
},
),
(
3u32,
SessionAuthorityInfo {
claps: 0,
disabled: false,
},
),
]);
assert_eq!(SlowClap::calculate_median_claps(&data, 4), 0);
});
}
#[test] #[test]
fn request_body_is_correct_for_get_block_number() { fn request_body_is_correct_for_get_block_number() {
let (offchain, _) = TestOffchainExt::new(); let (offchain, _) = TestOffchainExt::new();