diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index 887e92e..5895e0e 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.52" +version = "0.3.53" 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 62dcab0..4dd4b90 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -9,8 +9,7 @@ use frame_support::{ pallet_prelude::*, traits::{ tokens::fungible::{Inspect, Mutate}, - DisabledValidators, EstimateNextSessionRotation, Get, OneSessionHandler, ValidatorSet, - ValidatorSetWithIdentification, + DisabledValidators, Get, OneSessionHandler, ValidatorSet, ValidatorSetWithIdentification, }, WeakBoundedVec, }; @@ -35,11 +34,7 @@ use sp_staking::{ offence::{Kind, Offence, ReportOffence}, SessionIndex, }; -use sp_std::{ - collections::btree_map::BTreeMap, - prelude::*, - vec::Vec, -}; +use sp_std::{collections::btree_map::BTreeMap, prelude::*, vec::Vec}; use ghost_networks::{ NetworkData, NetworkDataBasicHandler, NetworkDataInspectHandler, NetworkDataMutateHandler, @@ -186,7 +181,6 @@ pub mod pallet { + MaybeSerializeDeserialize + MaxEncodedLen; - type NextSessionRotation: EstimateNextSessionRotation>; type ValidatorSet: ValidatorSetWithIdentification; type Currency: Inspect + Mutate; type NetworkDataHandler: NetworkDataBasicHandler @@ -215,12 +209,16 @@ pub mod pallet { #[pallet::constant] type HistoryDepth: Get; + #[pallet::constant] + type MinAuthoritiesNumber: Get; + type WeightInfo: WeightInfo; } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + BlackSwan, AuthoritiesEquilibrium, SomeAuthoritiesTrottling { throttling: Vec>, @@ -628,7 +626,8 @@ impl Pallet { let curr_received_claps_key = (curr_session_index, &transaction_hash, &clap_unique_hash); let mut previous_claps = ClapsInSession::::get(&prev_session_index); - let mut total_received_claps = ReceivedClaps::::get(&prev_received_claps_key).into_inner(); + let mut total_received_claps = + ReceivedClaps::::get(&prev_received_claps_key).into_inner(); for (auth_index, info) in ClapsInSession::::get(&curr_session_index).iter() { if !info.disabled { @@ -656,14 +655,9 @@ impl Pallet { } } - let disabled_authorities = previous_claps - .values() - .filter(|info| info.disabled) - .count(); + let disabled_authorities = previous_claps.values().filter(|info| info.disabled).count(); - let active_authorities = prev_authorities - .len() - .saturating_sub(disabled_authorities); + let active_authorities = prev_authorities.len().saturating_sub(disabled_authorities); let clap = Clap { authority_index: Default::default(), @@ -676,10 +670,9 @@ impl Pallet { amount, }; - let enough_authorities = Perbill::from_rational( - total_received_claps.len() as u32, - active_authorities as u32, - ) > Perbill::from_percent(T::ApplauseThreshold::get()); + let enough_authorities = + Perbill::from_rational(total_received_claps.len() as u32, active_authorities as u32) + > Perbill::from_percent(T::ApplauseThreshold::get()); ensure!(enough_authorities, Error::::NotEnoughClaps); Self::try_applause(&clap, &prev_received_claps_key)?; @@ -906,8 +899,8 @@ impl Pallet { EvmResponseType::TransactionLogs(evm_logs) => { if ClapsInSession::::get(&session_index) .get(&authority_index) - .map(|info| info.disabled) - .unwrap_or_default() + .map(|info| info.disabled) + .unwrap_or_default() { log::info!( target: LOG_TARGET, @@ -1225,8 +1218,21 @@ impl OneSessionHandler for Pallet { }) .collect::>>(); - if offenders.is_empty() { + let disabled_validators = T::DisabledValidators::disabled_validators() + .into_iter() + .count(); + + let offenders_length = offenders.len(); + let authorities_left: u32 = authorities_len + .saturating_sub(disabled_validators) + .saturating_sub(offenders_length) + .try_into() + .unwrap_or_default(); + + if offenders_length == 0 { Self::deposit_event(Event::::AuthoritiesEquilibrium); + } else if authorities_left < T::MinAuthoritiesNumber::get() { + Self::deposit_event(Event::::BlackSwan); } else { Self::deposit_event(Event::::SomeAuthoritiesTrottling { throttling: offenders.clone(), diff --git a/pallets/slow-clap/src/mock.rs b/pallets/slow-clap/src/mock.rs index 0df7757..befec45 100644 --- a/pallets/slow-clap/src/mock.rs +++ b/pallets/slow-clap/src/mock.rs @@ -3,7 +3,6 @@ use frame_support::{ derive_impl, parameter_types, traits::{ConstU32, ConstU64}, - weights::Weight, }; use frame_system::EnsureRoot; use pallet_session::historical as pallet_session_historical; @@ -140,27 +139,6 @@ parameter_types! { pub static MockAverageSessionLength: Option = None; } -pub struct TestNextSessionRotation; -impl frame_support::traits::EstimateNextSessionRotation for TestNextSessionRotation { - fn average_session_length() -> u64 { - let mock = MockAverageSessionLength::mutate(|p| p.take()); - mock.unwrap_or(pallet_session::PeriodicSessions::::average_session_length()) - } - - fn estimate_current_session_progress(now: u64) -> (Option, Weight) { - let (estimate, weight) = - pallet_session::PeriodicSessions::::estimate_current_session_progress( - now, - ); - let mock = MockCurrentSessionProgress::mutate(|p| p.take()); - (mock.unwrap_or(estimate), weight) - } - - fn estimate_next_session_rotation(now: u64) -> (Option, Weight) { - pallet_session::PeriodicSessions::::estimate_next_session_rotation(now) - } -} - impl ghost_networks::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; @@ -200,7 +178,6 @@ impl Config for Runtime { type RuntimeEvent = RuntimeEvent; type AuthorityId = UintAuthorityId; - type NextSessionRotation = TestNextSessionRotation; type ValidatorSet = Historical; type Currency = Balances; type NetworkDataHandler = Networks; @@ -210,9 +187,10 @@ impl Config for Runtime { type MaxAuthorities = ConstU32<5>; type ApplauseThreshold = ConstU32<50>; - type OffenceThreshold = ConstU32<75>; + type OffenceThreshold = ConstU32<0>; type UnsignedPriority = ConstU64<{ 1 << 20 }>; type HistoryDepth = HistoryDepth; + type MinAuthoritiesNumber = ConstU32<2>; type WeightInfo = (); } diff --git a/pallets/slow-clap/src/tests.rs b/pallets/slow-clap/src/tests.rs index a0d6068..9c41be3 100644 --- a/pallets/slow-clap/src/tests.rs +++ b/pallets/slow-clap/src/tests.rs @@ -979,7 +979,8 @@ fn should_self_applause_after_diabled() { let curr_session_index = Session::session_index(); pallet::ClapsInSession::::mutate(&session_index, |claps| { - claps.entry(1 as AuthIndex) + claps + .entry(1 as AuthIndex) .and_modify(|individual| (*individual).disabled = true) .or_insert(SessionAuthorityInfo { claps: 0u32, @@ -988,7 +989,8 @@ fn should_self_applause_after_diabled() { }); pallet::ClapsInSession::::mutate(&curr_session_index, |claps| { - claps.entry(2 as AuthIndex) + claps + .entry(2 as AuthIndex) .and_modify(|individual| (*individual).disabled = true) .or_insert(SessionAuthorityInfo { claps: 0u32, @@ -1207,6 +1209,22 @@ fn should_not_fail_on_sub_existential_balance() { }); } +#[test] +fn should_emit_black_swan_if_not_enough_authorities_left() { + let (network_id, _, _) = generate_unique_hash(None, None, None, None); + + new_test_ext().execute_with(|| { + let session_index = advance_session_and_get_index(); + assert_ok!(do_clap_from(session_index, network_id, 0, false)); + Session::disable_index(1); + Session::disable_index(2); + + advance_session(); + advance_session(); + System::assert_has_event(RuntimeEvent::SlowClap(crate::Event::BlackSwan)); + }); +} + fn advance_session_and_get_index() -> u32 { advance_session(); assert_eq!(Session::validators(), Vec::::new());