apply eviction logic during the offence preparation

Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
Uncle Stretch 2026-06-18 14:38:34 +03:00
parent 0dd27d6429
commit c112ad22c2
Signed by: str3tch
GPG Key ID: 84F3190747EE79AA
4 changed files with 68 additions and 2 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-slow-clap"
version = "0.4.29"
version = "0.4.30"
description = "Applause protocol for the EVM bridge"
license.workspace = true
authors.workspace = true

View File

@ -42,6 +42,7 @@ use ghost_networks::{
NetworkType,
};
use ghost_traits::exposure::ExposureListener;
use ghost_traits::evictor::StakingEvictor;
pub mod migrations;
pub mod weights;
@ -309,6 +310,7 @@ pub mod pallet {
>;
type DisabledValidators: DisabledValidators;
type ExposureListener: ExposureListener<BalanceOf<Self>, Self::AccountId>;
type StakingEvictor: StakingEvictor<Self::AccountId>;
#[pallet::constant]
type MaxAuthorities: Get<u32>;
@ -1455,7 +1457,15 @@ impl<T: Config> Pallet<T> {
let authority_index = index as AuthIndex;
if offence_bitmap.exists(&authority_index) {
weight.saturating_accrue(T::DbWeight::get().reads(2));
if let Some(account_id) = T::ExposureListener::get_account_by_index(index) {
weight.saturating_accrue(T::DbWeight::get().reads(1));
if T::StakingEvictor::try_evict_validator(&account_id) {
weight.saturating_accrue(T::DbWeight::get().writes(2));
}
}
if let Some(full_id) = <T::ValidatorSet as ValidatorSetWithIdentification<
T::AccountId,
>>::IdentificationOf::convert(id.clone())

View File

@ -5,6 +5,7 @@ use frame_support::{
traits::{ConstU32, ConstU64},
};
use frame_system::EnsureRoot;
use ghost_traits::evictor::StakingEvictor;
use pallet_session::historical as pallet_session_historical;
use sp_runtime::{
curve::PiecewiseLinear,
@ -36,6 +37,7 @@ frame_support::construct_runtime!(
);
parameter_types! {
pub static EvicatedValidators: Vec<u64> = vec![];
pub static FixedValidators: Vec<u64> = vec![0, 1, 2, 3];
}
@ -172,6 +174,25 @@ impl pallet_balances::Config for Runtime {
type Balance = u64;
pub struct TestStakingEvictor;
impl StakingEvictor<u64> for TestStakingEvictor {
fn try_evict_validator(who: &u64) -> bool {
if !FixedValidators::get().contains(who) {
return false;
}
let mut evicted_validators = EvicatedValidators::get();
match evicted_validators.iter().position(|x| x == who) {
Some(_) => false,
None => {
evicted_validators.push(*who);
EvicatedValidators::set(evicted_validators);
true
}
}
}
}
pub struct TestExposureListener;
impl ExposureListener<Balance, u64> for TestExposureListener
where
@ -217,6 +238,7 @@ impl Config for Runtime {
type ReportUnresponsiveness = OffenceHandler;
type DisabledValidators = Session;
type ExposureListener = TestExposureListener;
type StakingEvictor = TestStakingEvictor;
type MaxAuthorities = ConstU32<5>;
type ApplauseThreshold = ConstU32<500_000_000>;

View File

@ -1691,6 +1691,40 @@ fn migration_from_v2_to_v3_works_fine() {
});
}
#[test]
fn validators_are_evicted_during_offence() {
let (network_id, _, _) = generate_unique_hash(None, None, None, None, None);
new_test_ext().execute_with(|| {
let session_index = advance_session_and_get_index();
prepare_evm_network(None, None);
assert_eq!(BlockCommitments::<Runtime>::get(network_id).len(), 0);
System::set_block_number(5 * EpochDuration::get() / 2);
let last_stored_block = 69;
for i in 0..3 {
assert_ok!(do_block_commitment(
session_index,
network_id,
i,
last_stored_block,
));
}
assert_eq!(EvicatedValidators::get().len(), 0);
let current_block = SlowClap::current_block_number();
SlowClap::on_initialize(current_block);
System::assert_has_event(RuntimeEvent::SlowClap(
crate::Event::SomeAuthoritiesDelayed {
delayed: vec![(3, 3)],
},
));
assert_eq!(EvicatedValidators::get().len(), 1);
});
}
fn assert_clapped_amount(
session_index: &SessionIndex,
unique_hash: &H256,