Compare commits
6 Commits
4c4374c812
...
0e228d890d
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e228d890d | |||
| 5e17c12677 | |||
| c112ad22c2 | |||
| 0dd27d6429 | |||
| 8252107e96 | |||
| 6419d4ba16 |
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1186,7 +1186,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "casper-runtime"
|
||||
version = "3.5.41"
|
||||
version = "3.5.42"
|
||||
dependencies = [
|
||||
"casper-runtime-constants",
|
||||
"frame-benchmarking",
|
||||
@ -3838,7 +3838,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ghost-slow-clap"
|
||||
version = "0.4.28"
|
||||
version = "0.4.30"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
@ -3890,7 +3890,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ghost-traits"
|
||||
version = "0.3.32"
|
||||
version = "0.3.33"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"sp-runtime 31.0.1",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-slow-clap"
|
||||
version = "0.4.28"
|
||||
version = "0.4.30"
|
||||
description = "Applause protocol for the EVM bridge"
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@ -41,6 +41,7 @@ use ghost_networks::{
|
||||
NetworkData, NetworkDataBasicHandler, NetworkDataInspectHandler, NetworkDataMutateHandler,
|
||||
NetworkType,
|
||||
};
|
||||
use ghost_traits::evictor::StakingEvictor;
|
||||
use ghost_traits::exposure::ExposureListener;
|
||||
|
||||
pub mod migrations;
|
||||
@ -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(1));
|
||||
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())
|
||||
@ -1637,7 +1647,7 @@ impl<Offender: Clone> Offence<Offender> for SlowClapOffence<Offender> {
|
||||
missed_percent.saturating_mul(Perbill::from_rational(1, offenders_count))
|
||||
}
|
||||
OffenceType::CommitmentOffence => offenders_count
|
||||
.checked_sub(Perbill::from_percent(9).mul_ceil(self.validator_set_count))
|
||||
.checked_sub(Perbill::from_percent(9).mul_floor(self.validator_set_count))
|
||||
.map(|threshold| {
|
||||
Perbill::from_rational(threshold.saturating_mul(4), self.validator_set_count)
|
||||
.square()
|
||||
|
||||
@ -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>;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-traits"
|
||||
version = "0.3.32"
|
||||
version = "0.3.33"
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
3
pallets/traits/src/evictor.rs
Normal file
3
pallets/traits/src/evictor.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub trait StakingEvictor<AccountId> {
|
||||
fn try_evict_validator(who: &AccountId) -> bool;
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub mod evictor;
|
||||
pub mod exposure;
|
||||
pub mod networks;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "casper-runtime"
|
||||
version = "3.5.41"
|
||||
version = "3.5.42"
|
||||
build = "build.rs"
|
||||
description = "Runtime of the Casper Network"
|
||||
edition.workspace = true
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use super::*;
|
||||
use frame_support::{dispatch::DispatchResultWithPostInfo, traits::PrivilegeCmp};
|
||||
use ghost_traits::evictor::StakingEvictor;
|
||||
use ghost_traits::exposure::ExposureListener;
|
||||
use pallet_alliance::{ProposalIndex, ProposalProvider};
|
||||
use primitives::Balance;
|
||||
@ -78,6 +79,17 @@ impl PrivilegeCmp<OriginCaller> for EqualOrGreatestRootCmp {
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to evict validators and nominators from the staking pallet.
|
||||
pub struct RuntimeStakingEvictor<T>(PhantomData<T>);
|
||||
impl<T> StakingEvictor<AccountIdOf<T>> for RuntimeStakingEvictor<T>
|
||||
where
|
||||
T: pallet_staking::Config,
|
||||
{
|
||||
fn try_evict_validator(who: &AccountIdOf<T>) -> bool {
|
||||
pallet_staking::Pallet::<T>::do_remove_validator(who)
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to get the exposure information out of staking pallet directly.
|
||||
pub struct StakingExposureListener<T>(PhantomData<T>);
|
||||
impl<T> ExposureListener<Balance, AccountIdOf<T>> for StakingExposureListener<T>
|
||||
|
||||
@ -79,7 +79,10 @@ mod genesis_config_presets;
|
||||
mod impls;
|
||||
mod weights;
|
||||
|
||||
pub use impls::{AllianceProposalProvider, EqualOrGreatestRootCmp, StakingExposureListener};
|
||||
pub use impls::{
|
||||
AllianceProposalProvider, EqualOrGreatestRootCmp, RuntimeStakingEvictor,
|
||||
StakingExposureListener,
|
||||
};
|
||||
|
||||
// Governance configuration.
|
||||
pub mod cult;
|
||||
@ -119,8 +122,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("casper"),
|
||||
impl_name: create_runtime_str!("casper-svengali"),
|
||||
authoring_version: 0,
|
||||
spec_version: 6,
|
||||
impl_version: 4,
|
||||
spec_version: 7,
|
||||
impl_version: 5,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
state_version: 1,
|
||||
@ -1102,6 +1105,7 @@ impl ghost_slow_clap::Config for Runtime {
|
||||
type ReportUnresponsiveness = Offences;
|
||||
type DisabledValidators = Session;
|
||||
type ExposureListener = StakingExposureListener<Runtime>;
|
||||
type StakingEvictor = RuntimeStakingEvictor<Runtime>;
|
||||
|
||||
type ApplauseThreshold = ApplauseThreshold;
|
||||
|
||||
|
||||
@ -17,15 +17,21 @@ MORDOR_RPC=(
|
||||
"https://rpc.mordor.etccooperative.org"
|
||||
)
|
||||
|
||||
HOODI_RPC=(
|
||||
"https://rpc.hoodi.ethpandaops.io"
|
||||
"https://0xrpc.io/hoodi"
|
||||
"https://ethereum-hoodi.gateway.tatum.io"
|
||||
"https://rpc.sentio.xyz/hoodi"
|
||||
)
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: $(basename "$0") [OPTIONS]
|
||||
|
||||
Options:
|
||||
--chain <name> Specify network: 'sepolia' or 'mordor'. If omitted, checks both.
|
||||
--chain <name> Specify network: 'sepolia', 'hoodi' or 'mordor'. If omitted, checks all.
|
||||
--output <dir> Base directory to save results. Creates a subfolder with Unix Timestamp.
|
||||
--rpcs <urls> Comma-separated list of custom RPC endpoints.
|
||||
Disables default Sepolia/Mordor lists.
|
||||
--rpcs <urls> Comma-separated list of custom RPC endpoints. Disables default networks lists.
|
||||
--help Show this help message.
|
||||
|
||||
Example:
|
||||
@ -129,8 +135,11 @@ else
|
||||
check_network "Sepolia" "${SEPOLIA_RPC[@]}"
|
||||
elif [ "$CHOSEN_CHAIN" == "mordor" ]; then
|
||||
check_network "Mordor" "${MORDOR_RPC[@]}"
|
||||
elif [ "$CHOSEN_CHAIN" == "hoodi" ]; then
|
||||
check_network "Hoodi" "${HOODI_RPC[@]}"
|
||||
else
|
||||
check_network "Sepolia" "${SEPOLIA_RPC[@]}"
|
||||
check_network "Mordor" "${MORDOR_RPC[@]}"
|
||||
check_network "Hoodi" "${HOODI_RPC[@]}"
|
||||
fi
|
||||
fi
|
||||
|
||||
Loading…
Reference in New Issue
Block a user