Compare commits

...

6 Commits

Author SHA1 Message Date
6fa5e5ed97
update casper runtime based on new version of slow clap
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-11-12 20:00:57 +03:00
58c5f1f33d
apply latest version of slow clap
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-11-12 19:06:42 +03:00
55a77cd3d4
introduce black swan event e.g. disable everybody
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2025-11-12 19:03:58 +03:00
2cf4637d0c
update casper runtime version
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-11-10 21:23:08 +03:00
8123295f91
Merge branch 'pallet-slow-clap'
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-11-10 20:55:37 +03:00
092679eb0c
disable only claps during offchain worker but not the block gathering
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2025-11-10 20:54:37 +03:00
7 changed files with 72 additions and 64 deletions

4
Cargo.lock generated
View File

@ -1186,7 +1186,7 @@ dependencies = [
[[package]]
name = "casper-runtime"
version = "3.5.34"
version = "3.5.36"
dependencies = [
"casper-runtime-constants",
"frame-benchmarking",
@ -3836,7 +3836,7 @@ dependencies = [
[[package]]
name = "ghost-slow-clap"
version = "0.3.51"
version = "0.3.53"
dependencies = [
"frame-benchmarking",
"frame-support",

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-slow-clap"
version = "0.3.51"
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,
@ -118,7 +113,6 @@ enum OffchainErr<NetworkId> {
UnknownNetworkType(NetworkId),
OffchainTimeoutPeriod(NetworkId),
TooManyRequests(NetworkId),
AuthorityDisabled(AuthIndex),
}
impl<NetworkId: core::fmt::Debug> core::fmt::Debug for OffchainErr<NetworkId> {
@ -144,7 +138,6 @@ impl<NetworkId: core::fmt::Debug> core::fmt::Debug for OffchainErr<NetworkId> {
OffchainErr::UnknownNetworkType(ref network_id) => write!(fmt, "Unknown type for network #{:?}.", network_id),
OffchainErr::OffchainTimeoutPeriod(ref network_id) => write!(fmt, "Offchain request should be in-flight for network #{:?}.", network_id),
OffchainErr::TooManyRequests(ref network_id) => write!(fmt, "Too many requests over RPC endpoint for network #{:?}.", network_id),
OffchainErr::AuthorityDisabled(ref authority_index) => write!(fmt, "Authority index {:?} is disabled in current session.", authority_index),
}
}
}
@ -188,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
@ -217,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>>,
@ -630,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 {
@ -658,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(),
@ -678,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)?;
@ -768,14 +759,6 @@ impl<T: Config> Pallet<T> {
network_id: NetworkIdOf<T>,
network_data: &NetworkData,
) -> OffchainResult<T, ()> {
if ClapsInSession::<T>::get(&session_index)
.get(&authority_index)
.map(|info| info.disabled)
.unwrap_or_default()
{
return Err(OffchainErr::AuthorityDisabled(authority_index));
}
let network_id_encoded = network_id.encode();
let block_number_key = Self::create_storage_key(b"block-", &network_id_encoded);
@ -914,6 +897,20 @@ impl<T: Config> Pallet<T> {
Ok(Some(new_evm_block))
}
EvmResponseType::TransactionLogs(evm_logs) => {
if ClapsInSession::<T>::get(&session_index)
.get(&authority_index)
.map(|info| info.disabled)
.unwrap_or_default()
{
log::info!(
target: LOG_TARGET,
"🧐 Authority #{:?} disabled in session {:?}; no claps available",
authority_index,
session_index
);
return Ok(None);
}
let claps: Vec<_> = evm_logs
.iter()
.filter_map(|log| {
@ -1221,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());

View File

@ -1,6 +1,6 @@
[package]
name = "casper-runtime"
version = "3.5.34"
version = "3.5.36"
build = "build.rs"
description = "Runtime of the Casper Network"
edition.workspace = true

View File

@ -117,8 +117,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("casper"),
impl_name: create_runtime_str!("casper-svengali"),
authoring_version: 0,
spec_version: 4,
impl_version: 2,
spec_version: 6,
impl_version: 4,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
state_version: 1,
@ -1062,6 +1062,8 @@ parameter_types! {
pub const ApplauseThreshold: u32 = 66;
// will be used in `Perbill::from_percent()`
pub const OffenceThreshold: u32 = 5;
// 4 validators should be functional if 1 is offline
pub const MinAuthoritiesNumber: u32 = 5;
pub const SlowClapUnsignedPriority: TransactionPriority = TransactionPriority::MAX;
pub const SlowClapHistoryDepth: sp_staking::SessionIndex =
StakingHistoryDepth::get() * SessionsPerEra::get();
@ -1071,7 +1073,6 @@ impl ghost_slow_clap::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type AuthorityId = SlowClapId;
type NextSessionRotation = Babe;
type ValidatorSet = Historical;
type Currency = Balances;
type NetworkDataHandler = GhostNetworks;
@ -1084,6 +1085,7 @@ impl ghost_slow_clap::Config for Runtime {
type OffenceThreshold = OffenceThreshold;
type UnsignedPriority = SlowClapUnsignedPriority;
type HistoryDepth = SlowClapHistoryDepth;
type MinAuthoritiesNumber = MinAuthoritiesNumber;
type WeightInfo = weights::ghost_slow_clap::WeightInfo<Runtime>;
}