diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index 9f7ec79..177628c 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.35" +version = "0.3.36" 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 67be0a2..bfa9755 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -90,7 +90,7 @@ pub struct Clap { pub amount: Balance, } -#[derive(Default, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +#[derive(Default, Clone, Copy, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct SessionAuthorityInfo { pub claps: u32, pub disabled: bool, @@ -967,10 +967,18 @@ impl Pallet { .ok_or(OffchainErr::ErrorInEvmResponse)?) } - 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)) + fn calculate_median_claps( + actual_claps_in_session: &BTreeMap, + authorities_len: usize, + ) -> 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::>(); if claps_in_session.is_empty() { @@ -1083,9 +1091,10 @@ impl OneSessionHandler for Pallet { fn on_before_session_ending() { let session_index = T::ValidatorSet::session_index(); let validators = T::ValidatorSet::validators(); - let authorities = Authorities::::get(&session_index); + let authorities_len = Authorities::::get(&session_index).len(); + let actual_claps_in_session = ClapsInSession::::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 .into_iter() @@ -1105,7 +1114,7 @@ impl OneSessionHandler for Pallet { throttling: offenders.clone(), }); - let validator_set_count = authorities.len() as u32; + let validator_set_count = authorities_len as u32; let offence = ThrottlingOffence { session_index, validator_set_count, diff --git a/pallets/slow-clap/src/tests.rs b/pallets/slow-clap/src/tests.rs index 061b797..f4c9899 100644 --- a/pallets/slow-clap/src/tests.rs +++ b/pallets/slow-clap/src/tests.rs @@ -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] fn request_body_is_correct_for_get_block_number() { let (offchain, _) = TestOffchainExt::new();