From 46aa18aafe90c1436f8eecb22561706ed9cb9a8c Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Wed, 27 Aug 2025 14:41:46 +0300 Subject: [PATCH 01/13] implement cross session claim via self_applause Signed-off-by: Uncle Stinky --- pallets/slow-clap/Cargo.toml | 2 +- pallets/slow-clap/src/lib.rs | 56 +++++++++++++++++++++++++--------- pallets/slow-clap/src/tests.rs | 44 ++++++++++++++++++++------ 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index d0d47b4..071a066 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.40" +version = "0.3.41" 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 1e2eefd..e49bb1f 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -574,32 +574,55 @@ impl Pallet { fn applause_if_posible( network_id: NetworkIdOf, - session_index: SessionIndex, + prev_session_index: SessionIndex, transaction_hash: H256, receiver: T::AccountId, amount: BalanceOf, ) -> DispatchResult { + let curr_session_index = prev_session_index.saturating_add(1); let clap_unique_hash = Self::generate_unique_hash(&receiver, &amount, &network_id); - let received_claps_key = (session_index, &transaction_hash, &clap_unique_hash); + let prev_received_claps_key = (prev_session_index, &transaction_hash, &clap_unique_hash); + let curr_received_claps_key = (curr_session_index, &transaction_hash, &clap_unique_hash); + + let prev_authorities = Authorities::::get(&prev_session_index); + let curr_authorities = Authorities::::get(&curr_session_index); + + let prev_received_claps = ReceivedClaps::::get(&prev_received_claps_key).into_inner(); + let curr_received_claps = ReceivedClaps::::get(&curr_received_claps_key).into_inner(); + + let summary_authority_claps_length = curr_received_claps + .difference(&prev_received_claps) + .filter_map(|&index| { + curr_authorities + .get(index as usize) + .map(|curr_authority| { + prev_authorities + .iter() + .position(|prev_authority| curr_authority == prev_authority) + }) + .flatten() + }) + .count() + .saturating_add(curr_received_claps.len()); let clap = Clap { authority_index: Default::default(), block_number: Default::default(), - removed: false, - session_index, + removed: Default::default(), + session_index: Default::default(), + transaction_hash: Default::default(), network_id, receiver, amount, - transaction_hash, }; let enough_authorities = Perbill::from_rational( - ReceivedClaps::::get(&received_claps_key).len() as u32, - Authorities::::get(session_index).len() as u32, + summary_authority_claps_length as u32, + Authorities::::get(prev_session_index).len() as u32, ) > Perbill::from_percent(T::ApplauseThreshold::get()); ensure!(enough_authorities, Error::::NotEnoughClaps); - Self::try_applause(&clap, &received_claps_key)?; + Self::try_applause(&clap, &prev_received_claps_key)?; Ok(()) } @@ -766,14 +789,17 @@ impl Pallet { Some(_) if from_block.le(&to_block) => { let adjusted_to_block = estimated_block .checked_sub(from_block) - .map(|current_distance| current_distance - .le(&max_block_distance) - .then(|| estimated_block) - ) + .map(|current_distance| { + current_distance + .le(&max_block_distance) + .then(|| estimated_block) + }) .flatten() - .unwrap_or(from_block - .saturating_add(max_block_distance) - .min(estimated_block)); + .unwrap_or( + from_block + .saturating_add(max_block_distance) + .min(estimated_block), + ); (from_block, adjusted_to_block) } _ => (to_block, to_block), diff --git a/pallets/slow-clap/src/tests.rs b/pallets/slow-clap/src/tests.rs index 27370b8..13907bd 100644 --- a/pallets/slow-clap/src/tests.rs +++ b/pallets/slow-clap/src/tests.rs @@ -991,7 +991,6 @@ fn should_self_applause_if_enough_received_claps() { assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); - assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_eq!( pallet::ApplausesForTransaction::::get(&storage_key), @@ -999,14 +998,17 @@ fn should_self_applause_if_enough_received_claps() { ); assert_eq!(Balances::balance(&receiver), 0); - assert_ok!(SlowClap::self_applause( - RuntimeOrigin::signed(receiver), - network_id, - session_index, - transaction_hash, - receiver, - amount, - )); + assert_err!( + SlowClap::self_applause( + RuntimeOrigin::signed(receiver), + network_id, + session_index, + transaction_hash, + receiver, + amount, + ), + Error::::NotEnoughClaps + ); assert_eq!( pallet::ApplausesForTransaction::::get(&storage_key), false @@ -1015,6 +1017,26 @@ fn should_self_applause_if_enough_received_claps() { Networks::on_finalize(System::block_number()); + advance_session(); + let next_session_index = Session::session_index(); + let next_storage_key = ( + next_session_index, + transaction_hash, + unique_transaction_hash, + ); + assert_ok!(do_clap_from(next_session_index, network_id, 2, false)); + + assert_err!( + SlowClap::self_applause( + RuntimeOrigin::signed(receiver), + network_id, + next_session_index, + transaction_hash, + receiver, + amount, + ), + Error::::NotEnoughClaps + ); assert_ok!(SlowClap::self_applause( RuntimeOrigin::signed(receiver), network_id, @@ -1027,6 +1049,10 @@ fn should_self_applause_if_enough_received_claps() { pallet::ApplausesForTransaction::::get(&storage_key), true ); + assert_eq!( + pallet::ApplausesForTransaction::::get(&next_storage_key), + false + ); assert_eq!(Balances::balance(&receiver), amount); }); } From 7edc8935b6ad704f589023c275f852d1760dfbc1 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Wed, 27 Aug 2025 15:31:32 +0300 Subject: [PATCH 02/13] optimize offenders gathering with a single pass over the data Signed-off-by: Uncle Stinky --- pallets/slow-clap/Cargo.toml | 2 +- pallets/slow-clap/src/lib.rs | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index 071a066..49c68f1 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.41" +version = "0.3.42" 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 e49bb1f..f5b9f16 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -1123,11 +1123,13 @@ impl OneSessionHandler for Pallet { let offenders = validators .into_iter() .enumerate() - .filter(|(index, _)| !Self::is_good_actor(*index, median_claps, &claps_in_session)) - .filter_map(|(_, id)| { - >::IdentificationOf::convert( - id.clone(), - ).map(|full_id| (id, full_id)) + .filter_map(|(index, id)| { + (!Self::is_good_actor(index, median_claps, &claps_in_session)).then(|| { + >::IdentificationOf::convert( + id.clone(), + ).map(|full_id| (id, full_id)) + }) + .flatten() }) .collect::>>(); From 5307afe3520e30d0cc4ab58ded9fa716ce2d9268 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Wed, 27 Aug 2025 15:33:26 +0300 Subject: [PATCH 03/13] bump locked version Signed-off-by: Uncle Stinky --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4cc9873..d22ef9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3836,7 +3836,7 @@ dependencies = [ [[package]] name = "ghost-slow-clap" -version = "0.3.39" +version = "0.3.42" dependencies = [ "frame-benchmarking", "frame-support", From eb181c7f448f6f0fcba66b2b9310db1789a5908b Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Tue, 4 Nov 2025 17:43:36 +0300 Subject: [PATCH 04/13] fixes for the inconsistency of the slow claps Signed-off-by: Uncle Stinky --- pallets/slow-clap/Cargo.toml | 2 +- pallets/slow-clap/src/lib.rs | 75 +++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index 49c68f1..a499bcb 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.42" +version = "0.3.43" 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 f5b9f16..3e932ca 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -72,7 +72,7 @@ const LOG_TARGET: &str = "runtime::ghost-slow-clap"; const DB_PREFIX: &[u8] = b"slow_clap::"; const FETCH_TIMEOUT_PERIOD: u64 = 3_000; -const LOCK_BLOCK_EXPIRATION: u64 = 10; +const LOCK_BLOCK_EXPIRATION: u64 = 20; pub type AuthIndex = u32; @@ -378,7 +378,8 @@ pub mod pallet { fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { if let Call::slow_clap { clap, signature } = call { - let authorities = Authorities::::get(&clap.session_index); + let (session_index, _) = Self::mended_session_index(&clap); + let authorities = Authorities::::get(&session_index); let authority = match authorities.get(clap.authority_index as usize) { Some(authority) => authority, None => return InvalidTransaction::BadSigner.into(), @@ -459,27 +460,49 @@ impl Pallet { hex_str } - fn try_slow_clap(clap: &Clap, BalanceOf>) -> DispatchResult { - let authorities = Authorities::::get(&clap.session_index); - ensure!( - authorities.get(clap.authority_index as usize).is_some(), - Error::::NotAnAuthority + fn mended_session_index( + clap: &Clap, BalanceOf>, + ) -> (SessionIndex, H256) { + let prev_session_index = clap.session_index.saturating_sub(1); + let clap_unique_hash = + Self::generate_unique_hash(&clap.receiver, &clap.amount, &clap.network_id); + + let received_claps_key = ( + prev_session_index, + &clap.transaction_hash, + &clap_unique_hash, ); + + let session_index = ReceivedClaps::::get(&received_claps_key) + .is_empty() + .then(|| clap.session_index) + .unwrap_or(prev_session_index); + + (session_index, clap_unique_hash) + } + + fn try_slow_clap(clap: &Clap, BalanceOf>) -> DispatchResult { + let (session_index, clap_unique_hash) = Self::mended_session_index(&clap); + let mut claps_in_session = ClapsInSession::::get(&session_index); + ensure!( - ClapsInSession::::get(&clap.session_index) + claps_in_session .get(&clap.authority_index) .map(|info| !info.disabled) .unwrap_or(true), Error::::CurrentValidatorIsDisabled ); - let clap_unique_hash = - Self::generate_unique_hash(&clap.receiver, &clap.amount, &clap.network_id); - let received_claps_key = ( - clap.session_index, - &clap.transaction_hash, - &clap_unique_hash, - ); + let disabled_authorites = claps_in_session + .values() + .filter(|info| info.disabled) + .count(); + + let active_authorities = Authorities::::get(&session_index) + .len() + .saturating_sub(disabled_authorites); + + let received_claps_key = (session_index, &clap.transaction_hash, &clap_unique_hash); let number_of_received_claps = ReceivedClaps::::try_mutate(&received_claps_key, |tree_of_claps| { @@ -498,15 +521,15 @@ impl Pallet { } })?; - ClapsInSession::::mutate(&clap.session_index, |claps_details| { - (*claps_details) - .entry(clap.authority_index) - .and_modify(|individual| (*individual).claps.saturating_inc()) - .or_insert(SessionAuthorityInfo { - claps: 1u32, - disabled: false, - }); - }); + claps_in_session + .entry(clap.authority_index) + .and_modify(|individual| individual.claps.saturating_inc()) + .or_insert(SessionAuthorityInfo { + claps: 1u32, + disabled: false, + }); + + ClapsInSession::::insert(&session_index, claps_in_session); Self::deposit_event(Event::::Clapped { authority_id: clap.authority_index, @@ -517,7 +540,7 @@ impl Pallet { }); let enough_authorities = - Perbill::from_rational(number_of_received_claps as u32, authorities.len() as u32) + Perbill::from_rational(number_of_received_claps as u32, active_authorities as u32) > Perbill::from_percent(T::ApplauseThreshold::get()); if enough_authorities { @@ -1113,8 +1136,8 @@ impl OneSessionHandler for Pallet { } fn on_before_session_ending() { - let session_index = T::ValidatorSet::session_index(); let validators = T::ValidatorSet::validators(); + let session_index = T::ValidatorSet::session_index().saturating_sub(1); let authorities_len = Authorities::::get(&session_index).len(); let claps_in_session = ClapsInSession::::get(&session_index); From c872eca8ac8399dfa0f7ba36044635eaf7e1309b Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Wed, 5 Nov 2025 15:55:37 +0300 Subject: [PATCH 05/13] back look during the slashing Signed-off-by: Uncle Stinky --- pallets/slow-clap/Cargo.toml | 2 +- pallets/slow-clap/src/lib.rs | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index a499bcb..f8dea97 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.43" +version = "0.3.44" 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 3e932ca..a8c77b5 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -294,6 +294,16 @@ pub mod pallet { ValueQuery, >; + #[pallet::storage] + #[pallet::getter(fn validators)] + pub(super) type Validators = StorageMap< + _, + Twox64Concat, + SessionIndex, + WeakBoundedVec, T::MaxAuthorities>, + OptionQuery, + >; + #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { @@ -1075,16 +1085,23 @@ impl Pallet { let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities) .expect("more than the maximum number of authorities"); + let validators = T::ValidatorSet::validators(); + let bounded_validators = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(validators) + .expect("more than the maximum number of validators"); + if let Some(target_session_index) = session_index.checked_sub(T::HistoryDepth::get()) { Self::clear_history(&target_session_index); } + Validators::::insert(&session_index, bounded_validators); Authorities::::set(&session_index, bounded_authorities); ClapsInSession::::set(&session_index, Default::default()); } fn clear_history(target_session_index: &SessionIndex) { ClapsInSession::::remove(target_session_index); + Authorities::::remove(target_session_index); + Validators::::remove(target_session_index); let mut cursor = ReceivedClaps::::clear_prefix((target_session_index,), u32::MAX, None); debug_assert!(cursor.maybe_cursor.is_none()); cursor = @@ -1136,8 +1153,8 @@ impl OneSessionHandler for Pallet { } fn on_before_session_ending() { - let validators = T::ValidatorSet::validators(); let session_index = T::ValidatorSet::session_index().saturating_sub(1); + let validators = Validators::::get(&session_index).unwrap_or_default(); let authorities_len = Authorities::::get(&session_index).len(); let claps_in_session = ClapsInSession::::get(&session_index); From ebae9fadbe4b0737a9f1aa7e595980cd5d2a1cd9 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Wed, 5 Nov 2025 20:52:00 +0300 Subject: [PATCH 06/13] fixes for self_applause function Signed-off-by: Uncle Stinky --- pallets/slow-clap/Cargo.toml | 2 +- pallets/slow-clap/src/lib.rs | 53 +++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index f8dea97..1fbb067 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.44" +version = "0.3.45" 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 a8c77b5..e28a04e 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -35,7 +35,11 @@ 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, btree_set::BTreeSet}, + prelude::*, + vec::Vec, +}; use ghost_networks::{ NetworkData, NetworkDataBasicHandler, NetworkDataInspectHandler, NetworkDataMutateHandler, @@ -238,7 +242,6 @@ pub mod pallet { #[pallet::error] pub enum Error { NotEnoughClaps, - NotAnAuthority, CurrentValidatorIsDisabled, AlreadyClapped, UnregisteredClapRemove, @@ -340,7 +343,7 @@ pub mod pallet { pub fn self_applause( origin: OriginFor, network_id: NetworkIdOf, - session_index: SessionIndex, + prev_session_index: SessionIndex, transaction_hash: H256, receiver: T::AccountId, amount: BalanceOf, @@ -348,7 +351,7 @@ pub mod pallet { let _ = ensure_signed(origin)?; Self::applause_if_posible( network_id, - session_index, + prev_session_index, transaction_hash, receiver, amount, @@ -614,29 +617,35 @@ impl Pallet { ) -> DispatchResult { let curr_session_index = prev_session_index.saturating_add(1); let clap_unique_hash = Self::generate_unique_hash(&receiver, &amount, &network_id); - let prev_received_claps_key = (prev_session_index, &transaction_hash, &clap_unique_hash); - let curr_received_claps_key = (curr_session_index, &transaction_hash, &clap_unique_hash); let prev_authorities = Authorities::::get(&prev_session_index); let curr_authorities = Authorities::::get(&curr_session_index); - let prev_received_claps = ReceivedClaps::::get(&prev_received_claps_key).into_inner(); - let curr_received_claps = ReceivedClaps::::get(&curr_received_claps_key).into_inner(); + let prev_received_claps_key = (prev_session_index, &transaction_hash, &clap_unique_hash); + let curr_received_claps_key = (curr_session_index, &transaction_hash, &clap_unique_hash); + + let prev_received_claps = ReceivedClaps::::get(&prev_received_claps_key) + .into_iter() + .filter_map(|auth_index| prev_authorities.get(auth_index as usize)) + .cloned() + .collect::>(); + + let curr_received_claps = ReceivedClaps::::get(&curr_received_claps_key) + .into_iter() + .filter_map(|auth_index| curr_authorities.get(auth_index as usize)) + .cloned() + .collect::>(); + + let disabled_authorites = ClapsInSession::::get(&prev_session_index) + .values() + .filter(|info| info.disabled) + .count(); + + let active_authorities = prev_authorities.len().saturating_sub(disabled_authorites); let summary_authority_claps_length = curr_received_claps - .difference(&prev_received_claps) - .filter_map(|&index| { - curr_authorities - .get(index as usize) - .map(|curr_authority| { - prev_authorities - .iter() - .position(|prev_authority| curr_authority == prev_authority) - }) - .flatten() - }) - .count() - .saturating_add(curr_received_claps.len()); + .symmetric_difference(&prev_received_claps) + .count(); let clap = Clap { authority_index: Default::default(), @@ -651,7 +660,7 @@ impl Pallet { let enough_authorities = Perbill::from_rational( summary_authority_claps_length as u32, - Authorities::::get(prev_session_index).len() as u32, + active_authorities as u32, ) > Perbill::from_percent(T::ApplauseThreshold::get()); ensure!(enough_authorities, Error::::NotEnoughClaps); From decf6665dfc0eceaac76f7c3fb38e809c530a3f2 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Wed, 5 Nov 2025 20:55:04 +0300 Subject: [PATCH 07/13] make slow-clap tests work again based on new functionality, new tests added too Signed-off-by: Uncle Stinky --- pallets/slow-clap/src/tests.rs | 140 ++++++++++++++++----------------- 1 file changed, 66 insertions(+), 74 deletions(-) diff --git a/pallets/slow-clap/src/tests.rs b/pallets/slow-clap/src/tests.rs index 13907bd..47b859b 100644 --- a/pallets/slow-clap/src/tests.rs +++ b/pallets/slow-clap/src/tests.rs @@ -665,15 +665,14 @@ fn should_throw_error_if_session_index_is_not_current() { network_id, authority_prev )); - assert_ok!(do_clap_from_first_authority( - session_index_next, - network_id, - authority_next - )); + assert_err!( + do_clap_from_first_authority(session_index_next, network_id, authority_next), + DispatchError::Other("Transaction has a bad signature") + ); assert_claps_info_correct(&storage_key_curr, &session_index_curr, 1); assert_claps_info_correct(&storage_key_prev, &session_index_prev, 1); - assert_claps_info_correct(&storage_key_next, &session_index_next, 1); + assert_claps_info_correct(&storage_key_next, &session_index_next, 0); } }); } @@ -688,22 +687,12 @@ fn should_throw_error_if_signer_has_incorrect_index() { let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_claps_info_correct(&storage_key, &session_index, 0); - let clap = Clap { - block_number: 420, - removed: false, - transaction_hash, - session_index, - authority_index: 1337, - network_id, - receiver: 69, - amount: 420, - }; - let authority = UintAuthorityId::from((1) as u64); - let signature = authority.sign(&clap.encode()).unwrap(); - assert_err!( - SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature), - Error::::NotAnAuthority - ); + assert_invalid_signing_address(session_index, network_id, 69); + assert_transaction_has_bad_signature(session_index, network_id, 69); + assert_invalid_signing_address(session_index, network_id, 420); + assert_transaction_has_bad_signature(session_index, network_id, 420); + assert_invalid_signing_address(session_index, network_id, 1337); + assert_transaction_has_bad_signature(session_index, network_id, 1337); assert_claps_info_correct(&storage_key, &session_index, 0); }); } @@ -956,8 +945,8 @@ fn should_avoid_applause_during_nullification_period() { } #[test] -fn should_self_applause_if_enough_received_claps() { - let zero: u64 = 0u64; +fn should_self_applause_if_enough_claps() { + let zero = 0u64; let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, None, None); let (_, receiver, amount) = get_mocked_metadata(); @@ -965,6 +954,7 @@ fn should_self_applause_if_enough_received_claps() { new_test_ext().execute_with(|| { let _ = prepare_evm_network(Some(network_id), Some(0)); let session_index = advance_session_and_get_index(); + let next_session_index = session_index.saturating_add(1); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_err!( @@ -976,7 +966,7 @@ fn should_self_applause_if_enough_received_claps() { receiver, amount, ), - Error::::NotEnoughClaps + Error::::NotEnoughClaps, ); assert_eq!( @@ -984,59 +974,34 @@ fn should_self_applause_if_enough_received_claps() { false ); assert_eq!(Balances::balance(&receiver), zero); - assert_eq!( - BridgedInflationCurve::::era_payout(zero, zero, zero), - (zero, zero) - ); assert_ok!(do_clap_from(session_index, network_id, 0, false)); - assert_ok!(do_clap_from(session_index, network_id, 1, false)); - - assert_eq!( - pallet::ApplausesForTransaction::::get(&storage_key), - false - ); - assert_eq!(Balances::balance(&receiver), 0); - - assert_err!( - SlowClap::self_applause( - RuntimeOrigin::signed(receiver), - network_id, - session_index, - transaction_hash, - receiver, - amount, - ), - Error::::NotEnoughClaps - ); - assert_eq!( - pallet::ApplausesForTransaction::::get(&storage_key), - false - ); - assert_eq!(Balances::balance(&receiver), 0); - - Networks::on_finalize(System::block_number()); - advance_session(); - let next_session_index = Session::session_index(); - let next_storage_key = ( - next_session_index, - transaction_hash, - unique_transaction_hash, - ); - assert_ok!(do_clap_from(next_session_index, network_id, 2, false)); - assert_err!( - SlowClap::self_applause( - RuntimeOrigin::signed(receiver), - network_id, - next_session_index, - transaction_hash, - receiver, - amount, - ), - Error::::NotEnoughClaps + let mut fake_claps_in_session = sp_std::collections::btree_map::BTreeMap::new(); + fake_claps_in_session.insert( + 1, + SessionAuthorityInfo { + claps: 1, + disabled: false, + }, ); + fake_claps_in_session.insert( + 2, + SessionAuthorityInfo { + claps: 1, + disabled: false, + }, + ); + + let mut fake_received_clap = + BoundedBTreeSet::::MaxAuthorities>::new(); + assert_eq!(fake_received_clap.try_insert(1).unwrap(), true); + assert_eq!(fake_received_clap.try_insert(2).unwrap(), true); + + pallet::ReceivedClaps::::insert(&storage_key, fake_received_clap); + pallet::ClapsInSession::::insert(&next_session_index, fake_claps_in_session); + assert_ok!(SlowClap::self_applause( RuntimeOrigin::signed(receiver), network_id, @@ -1049,10 +1014,37 @@ fn should_self_applause_if_enough_received_claps() { pallet::ApplausesForTransaction::::get(&storage_key), true ); + assert_eq!(Balances::balance(&receiver), amount); + }); +} + +#[test] +fn should_avoid_session_overlap_on_mended_session_index() { + let (network_id, transaction_hash, unique_transaction_hash) = + generate_unique_hash(None, None, None, None); + let (_, receiver, amount) = get_mocked_metadata(); + + new_test_ext().execute_with(|| { + let _ = prepare_evm_network(Some(network_id), Some(0)); + let session_index = advance_session_and_get_index(); + let storage_key = (session_index, transaction_hash, unique_transaction_hash); + + assert_ok!(do_clap_from(session_index, network_id, 0, false)); + advance_session(); + assert_eq!( - pallet::ApplausesForTransaction::::get(&next_storage_key), + pallet::ApplausesForTransaction::::get(&storage_key), false ); + assert_eq!(Balances::balance(&receiver), 0u64); + + assert_ok!(do_clap_from(session_index, network_id, 1, false)); + assert_ok!(do_clap_from(session_index, network_id, 2, false)); + + assert_eq!( + pallet::ApplausesForTransaction::::get(&storage_key), + true + ); assert_eq!(Balances::balance(&receiver), amount); }); } From d6ecdcd4681e497a381cbb0fe966964e1b58cd61 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Wed, 5 Nov 2025 22:13:28 +0300 Subject: [PATCH 08/13] correct benchmarking of self applause and remove of unnecessary storage during tests Signed-off-by: Uncle Stinky --- pallets/slow-clap/src/benchmarking.rs | 21 +++++++++++++++++---- pallets/slow-clap/src/tests.rs | 18 ------------------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/pallets/slow-clap/src/benchmarking.rs b/pallets/slow-clap/src/benchmarking.rs index 20ca428..432ce5f 100644 --- a/pallets/slow-clap/src/benchmarking.rs +++ b/pallets/slow-clap/src/benchmarking.rs @@ -50,10 +50,15 @@ benchmarks! { self_applause { let session_index = T::ValidatorSet::session_index(); - let authorities = vec![T::AuthorityId::generate_pair(None)]; + let next_session_index = session_index.saturating_add(1); + let authorities = vec![ + T::AuthorityId::generate_pair(None), + T::AuthorityId::generate_pair(None), + ]; let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone()) .map_err(|()| "more than the maximum number of keys provided")?; - Authorities::::set(&session_index, bounded_authorities); + Authorities::::set(&session_index, bounded_authorities.clone()); + Authorities::::set(&next_session_index, bounded_authorities); let minimum_balance = <::Currency>::minimum_balance(); let receiver = create_account::(); @@ -68,6 +73,7 @@ benchmarks! { &network_id, ); let storage_key = (session_index, &transaction_hash, &unique_transaction_hash); + let next_storage_key = (next_session_index, &transaction_hash, &unique_transaction_hash); >::trigger_nullification_for_benchmark(); let clap = Clap { @@ -86,11 +92,18 @@ benchmarks! { .expect("first authority should exist"); let encoded_clap = clap.encode(); let signature = authority_id.sign(&encoded_clap).unwrap(); - >::slow_clap(RawOrigin::None.into(), clap, signature)?; - >::trigger_nullification_for_benchmark(); + Pallet::::slow_clap(RawOrigin::None.into(), clap, signature)?; + Pallet::::trigger_nullification_for_benchmark(); assert_eq!(<::Currency>::total_balance(&receiver), Default::default()); assert_eq!(ApplausesForTransaction::::get(&storage_key), false); + + frame_system::Pallet::::on_initialize(1u32.into()); + + let mut fake_received_clap = + BoundedBTreeSet::::new(); + assert_eq!(fake_received_clap.try_insert(1).unwrap(), true); + pallet::ReceivedClaps::::insert(&next_storage_key, fake_received_clap); }: _(RawOrigin::Signed(receiver_clone), network_id, session_index, transaction_hash, receiver_clone.clone(), amount) verify { assert_eq!(<::Currency>::total_balance(&receiver), amount); diff --git a/pallets/slow-clap/src/tests.rs b/pallets/slow-clap/src/tests.rs index 47b859b..4ea4424 100644 --- a/pallets/slow-clap/src/tests.rs +++ b/pallets/slow-clap/src/tests.rs @@ -954,7 +954,6 @@ fn should_self_applause_if_enough_claps() { new_test_ext().execute_with(|| { let _ = prepare_evm_network(Some(network_id), Some(0)); let session_index = advance_session_and_get_index(); - let next_session_index = session_index.saturating_add(1); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_err!( @@ -978,29 +977,12 @@ fn should_self_applause_if_enough_claps() { assert_ok!(do_clap_from(session_index, network_id, 0, false)); advance_session(); - let mut fake_claps_in_session = sp_std::collections::btree_map::BTreeMap::new(); - fake_claps_in_session.insert( - 1, - SessionAuthorityInfo { - claps: 1, - disabled: false, - }, - ); - fake_claps_in_session.insert( - 2, - SessionAuthorityInfo { - claps: 1, - disabled: false, - }, - ); - let mut fake_received_clap = BoundedBTreeSet::::MaxAuthorities>::new(); assert_eq!(fake_received_clap.try_insert(1).unwrap(), true); assert_eq!(fake_received_clap.try_insert(2).unwrap(), true); pallet::ReceivedClaps::::insert(&storage_key, fake_received_clap); - pallet::ClapsInSession::::insert(&next_session_index, fake_claps_in_session); assert_ok!(SlowClap::self_applause( RuntimeOrigin::signed(receiver), From 6acec08fa5251a94fe3c8ce60152a8cfa85a7f09 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Thu, 6 Nov 2025 14:32:04 +0300 Subject: [PATCH 09/13] increase storage version for the pallet Signed-off-by: Uncle Stinky --- pallets/slow-clap/Cargo.toml | 2 +- pallets/slow-clap/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index 1fbb067..4ed36e2 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.45" +version = "0.3.46" 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 e28a04e..71cb56d 100644 --- a/pallets/slow-clap/src/lib.rs +++ b/pallets/slow-clap/src/lib.rs @@ -170,7 +170,7 @@ type OffchainResult = Result>>; pub mod pallet { use super::*; - const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] From af14543cc10bc0cf234fa1e50022f468c42d2821 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Thu, 6 Nov 2025 14:33:25 +0300 Subject: [PATCH 10/13] change casper runtime versioning Signed-off-by: Uncle Stinky --- runtime/casper/Cargo.toml | 2 +- runtime/casper/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/casper/Cargo.toml b/runtime/casper/Cargo.toml index 0c74906..bf56843 100644 --- a/runtime/casper/Cargo.toml +++ b/runtime/casper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "casper-runtime" -version = "3.5.31" +version = "3.5.32" build = "build.rs" description = "Runtime of the Casper Network" edition.workspace = true diff --git a/runtime/casper/src/lib.rs b/runtime/casper/src/lib.rs index 42e2504..d5f9cb8 100644 --- a/runtime/casper/src/lib.rs +++ b/runtime/casper/src/lib.rs @@ -117,11 +117,11 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("casper"), impl_name: create_runtime_str!("casper-svengali"), authoring_version: 0, - spec_version: 2, - impl_version: 0, + spec_version: 3, + impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 0, + state_version: 1, }; /// The BABE epoch configuration at genesis. From 18241ea504cea1517b48dde9ccdc93e7618800cf Mon Sep 17 00:00:00 2001 From: Uncle Stretch Date: Thu, 6 Nov 2025 14:59:27 +0300 Subject: [PATCH 11/13] update weights for the slow clap Signed-off-by: Uncle Stretch --- pallets/slow-clap/Cargo.toml | 2 +- pallets/slow-clap/src/weights.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/slow-clap/Cargo.toml b/pallets/slow-clap/Cargo.toml index 4ed36e2..0bb443e 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.46" +version = "0.3.47" description = "Applause protocol for the EVM bridge" license.workspace = true authors.workspace = true diff --git a/pallets/slow-clap/src/weights.rs b/pallets/slow-clap/src/weights.rs index 06180f8..f103640 100644 --- a/pallets/slow-clap/src/weights.rs +++ b/pallets/slow-clap/src/weights.rs @@ -16,7 +16,7 @@ //! Autogenerated weights for `ghost_slow_clap` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2025-06-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2025-11-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024 From cc566ac5490f56463a2031145e43df8932147dd8 Mon Sep 17 00:00:00 2001 From: Uncle Stretch Date: Thu, 6 Nov 2025 15:01:01 +0300 Subject: [PATCH 12/13] update weights for slow clap in casper runtime Signed-off-by: Uncle Stretch --- runtime/casper/Cargo.toml | 2 +- runtime/casper/src/weights/ghost_slow_clap.rs | 36 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/runtime/casper/Cargo.toml b/runtime/casper/Cargo.toml index bf56843..104bbe1 100644 --- a/runtime/casper/Cargo.toml +++ b/runtime/casper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "casper-runtime" -version = "3.5.32" +version = "3.5.33" build = "build.rs" description = "Runtime of the Casper Network" edition.workspace = true diff --git a/runtime/casper/src/weights/ghost_slow_clap.rs b/runtime/casper/src/weights/ghost_slow_clap.rs index 6530833..2fe78ca 100644 --- a/runtime/casper/src/weights/ghost_slow_clap.rs +++ b/runtime/casper/src/weights/ghost_slow_clap.rs @@ -16,7 +16,7 @@ //! Autogenerated weights for `ghost_slow_clap` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2025-06-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2025-11-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `ghostown`, CPU: `Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("casper-dev")`, DB CACHE: 1024 @@ -46,12 +46,12 @@ use core::marker::PhantomData; /// Weight functions for `ghost_slow_clap`. pub struct WeightInfo(PhantomData); impl ghost_slow_clap::WeightInfo for WeightInfo { - /// Storage: `GhostSlowClaps::Authorities` (r:1 w:0) - /// Proof: `GhostSlowClaps::Authorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `GhostSlowClaps::ReceivedClaps` (r:1 w:1) /// Proof: `GhostSlowClaps::ReceivedClaps` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `GhostSlowClaps::ClapsInSession` (r:1 w:1) /// Proof: `GhostSlowClaps::ClapsInSession` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `GhostSlowClaps::Authorities` (r:1 w:0) + /// Proof: `GhostSlowClaps::Authorities` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `GhostSlowClaps::ApplausesForTransaction` (r:1 w:1) /// Proof: `GhostSlowClaps::ApplausesForTransaction` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `GhostNetworks::NullifyNeeded` (r:1 w:0) @@ -68,18 +68,20 @@ impl ghost_slow_clap::WeightInfo for WeightInfo { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn slow_clap() -> Weight { // Proof Size summary in bytes: - // Measured: `355` - // Estimated: `3820` - // Minimum execution time: 213_817_000 picoseconds. - Weight::from_parts(216_977_000, 0) - .saturating_add(Weight::from_parts(0, 3820)) + // Measured: `387` + // Estimated: `3852` + // Minimum execution time: 220_008_000 picoseconds. + Weight::from_parts(223_058_000, 0) + .saturating_add(Weight::from_parts(0, 3852)) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(7)) } - /// Storage: `GhostSlowClaps::ReceivedClaps` (r:1 w:0) - /// Proof: `GhostSlowClaps::ReceivedClaps` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `GhostSlowClaps::Authorities` (r:1 w:0) + /// Storage: `GhostSlowClaps::Authorities` (r:2 w:0) /// Proof: `GhostSlowClaps::Authorities` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `GhostSlowClaps::ReceivedClaps` (r:2 w:0) + /// Proof: `GhostSlowClaps::ReceivedClaps` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `GhostSlowClaps::ClapsInSession` (r:1 w:0) + /// Proof: `GhostSlowClaps::ClapsInSession` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `GhostSlowClaps::ApplausesForTransaction` (r:1 w:1) /// Proof: `GhostSlowClaps::ApplausesForTransaction` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `GhostNetworks::NullifyNeeded` (r:1 w:0) @@ -96,12 +98,12 @@ impl ghost_slow_clap::WeightInfo for WeightInfo { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn self_applause() -> Weight { // Proof Size summary in bytes: - // Measured: `655` - // Estimated: `4120` - // Minimum execution time: 210_676_000 picoseconds. - Weight::from_parts(212_905_000, 0) - .saturating_add(Weight::from_parts(0, 4120)) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `861` + // Estimated: `6801` + // Minimum execution time: 258_510_000 picoseconds. + Weight::from_parts(262_353_000, 0) + .saturating_add(Weight::from_parts(0, 6801)) + .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(5)) } } From 0e750c0261dfcff84efe7d6089c7bf543f2e4918 Mon Sep 17 00:00:00 2001 From: Uncle Stretch Date: Thu, 6 Nov 2025 16:53:19 +0300 Subject: [PATCH 13/13] update lock file Signed-off-by: Uncle Stretch --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d22ef9d..c0f8438 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1186,7 +1186,7 @@ dependencies = [ [[package]] name = "casper-runtime" -version = "3.5.31" +version = "3.5.33" dependencies = [ "casper-runtime-constants", "frame-benchmarking", @@ -3836,7 +3836,7 @@ dependencies = [ [[package]] name = "ghost-slow-clap" -version = "0.3.42" +version = "0.3.47" dependencies = [ "frame-benchmarking", "frame-support",