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]
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

View File

@ -90,7 +90,7 @@ pub struct Clap<AccountId, NetworkId, Balance> {
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<T: Config> Pallet<T> {
.ok_or(OffchainErr::ErrorInEvmResponse)?)
}
fn calculate_median_claps(session_index: &SessionIndex) -> u32 {
let mut claps_in_session = ClapsInSession::<T>::get(session_index)
.values()
.filter_map(|value| (!value.disabled).then(|| value.claps))
fn calculate_median_claps(
actual_claps_in_session: &BTreeMap<AuthIndex, SessionAuthorityInfo>,
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::<Vec<_>>();
if claps_in_session.is_empty() {
@ -1083,9 +1091,10 @@ impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
fn on_before_session_ending() {
let session_index = T::ValidatorSet::session_index();
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
.into_iter()
@ -1105,7 +1114,7 @@ impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
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,

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]
fn request_body_is_correct_for_get_block_number() {
let (offchain, _) = TestOffchainExt::new();