introduce black swan event e.g. disable everybody

Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
Uncle Stinky 2025-11-12 19:03:58 +03:00
parent 092679eb0c
commit 55a77cd3d4
Signed by: st1nky
GPG Key ID: 016064BD97603B40
4 changed files with 52 additions and 50 deletions

View File

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

View File

@ -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<BlockNumberFor<Self>>;
type ValidatorSet: ValidatorSetWithIdentification<Self::AccountId>;
type Currency: Inspect<Self::AccountId> + Mutate<Self::AccountId>;
type NetworkDataHandler: NetworkDataBasicHandler
@ -215,12 +209,16 @@ pub mod pallet {
#[pallet::constant]
type HistoryDepth: Get<SessionIndex>;
#[pallet::constant]
type MinAuthoritiesNumber: Get<u32>;
type WeightInfo: WeightInfo;
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
BlackSwan,
AuthoritiesEquilibrium,
SomeAuthoritiesTrottling {
throttling: Vec<IdentificationTuple<T>>,
@ -628,7 +626,8 @@ impl<T: Config> Pallet<T> {
let curr_received_claps_key = (curr_session_index, &transaction_hash, &clap_unique_hash);
let mut previous_claps = ClapsInSession::<T>::get(&prev_session_index);
let mut total_received_claps = ReceivedClaps::<T>::get(&prev_received_claps_key).into_inner();
let mut total_received_claps =
ReceivedClaps::<T>::get(&prev_received_claps_key).into_inner();
for (auth_index, info) in ClapsInSession::<T>::get(&curr_session_index).iter() {
if !info.disabled {
@ -656,14 +655,9 @@ impl<T: Config> Pallet<T> {
}
}
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<T: Config> Pallet<T> {
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::<T>::NotEnoughClaps);
Self::try_applause(&clap, &prev_received_claps_key)?;
@ -906,8 +899,8 @@ impl<T: Config> Pallet<T> {
EvmResponseType::TransactionLogs(evm_logs) => {
if ClapsInSession::<T>::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<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
})
.collect::<Vec<IdentificationTuple<T>>>();
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::<T>::AuthoritiesEquilibrium);
} else if authorities_left < T::MinAuthoritiesNumber::get() {
Self::deposit_event(Event::<T>::BlackSwan);
} else {
Self::deposit_event(Event::<T>::SomeAuthoritiesTrottling {
throttling: offenders.clone(),

View File

@ -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<u64> = None;
}
pub struct TestNextSessionRotation;
impl frame_support::traits::EstimateNextSessionRotation<u64> for TestNextSessionRotation {
fn average_session_length() -> u64 {
let mock = MockAverageSessionLength::mutate(|p| p.take());
mock.unwrap_or(pallet_session::PeriodicSessions::<Period, Offset>::average_session_length())
}
fn estimate_current_session_progress(now: u64) -> (Option<Permill>, Weight) {
let (estimate, weight) =
pallet_session::PeriodicSessions::<Period, Offset>::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<u64>, Weight) {
pallet_session::PeriodicSessions::<Period, Offset>::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 = ();
}

View File

@ -979,7 +979,8 @@ fn should_self_applause_after_diabled() {
let curr_session_index = Session::session_index();
pallet::ClapsInSession::<Runtime>::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::<Runtime>::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::<u64>::new());