From 56083b85df98a7802c1276644a75263b50ea5408 Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Fri, 22 Sep 2023 01:26:47 +1200 Subject: [PATCH] feat: Witnesser dispatch call filter (#4001) Co-authored-by: Daniel Co-authored-by: dandanlen <3168260+dandanlen@users.noreply.github.com> --- Cargo.lock | 1 + .../cf-integration-tests/src/authorities.rs | 2 +- state-chain/pallets/cf-witnesser/src/lib.rs | 106 ++++++++++------ state-chain/pallets/cf-witnesser/src/mock.rs | 24 +++- state-chain/pallets/cf-witnesser/src/tests.rs | 35 ++++++ state-chain/runtime/Cargo.toml | 2 + state-chain/runtime/src/chainflip.rs | 23 +--- state-chain/runtime/src/lib.rs | 40 +++--- state-chain/runtime/src/safe_mode.rs | 117 ++++++++++++++++++ state-chain/traits/src/lib.rs | 10 ++ 10 files changed, 278 insertions(+), 82 deletions(-) create mode 100644 state-chain/runtime/src/safe_mode.rs diff --git a/Cargo.lock b/Cargo.lock index c8ce548cf3..13d0a2ebe8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11426,6 +11426,7 @@ dependencies = [ "cf-amm", "cf-chains", "cf-primitives", + "cf-runtime-utilities", "cf-session-benchmarking", "cf-test-utilities", "cf-traits", diff --git a/state-chain/cf-integration-tests/src/authorities.rs b/state-chain/cf-integration-tests/src/authorities.rs index 940de5d7d9..4ef96a1c82 100644 --- a/state-chain/cf-integration-tests/src/authorities.rs +++ b/state-chain/cf-integration-tests/src/authorities.rs @@ -12,7 +12,7 @@ use cf_traits::{AsyncResult, EpochInfo, SafeMode, VaultRotator, VaultStatus}; use pallet_cf_environment::SafeModeUpdate; use pallet_cf_validator::{CurrentRotationPhase, RotationPhase}; use state_chain_runtime::{ - chainflip::RuntimeSafeMode, BitcoinVault, Environment, EthereumInstance, EthereumVault, Flip, + safe_mode::RuntimeSafeMode, BitcoinVault, Environment, EthereumInstance, EthereumVault, Flip, PolkadotInstance, PolkadotVault, Runtime, RuntimeOrigin, Validator, }; diff --git a/state-chain/pallets/cf-witnesser/src/lib.rs b/state-chain/pallets/cf-witnesser/src/lib.rs index c2b9c9d7d7..ae19005b03 100644 --- a/state-chain/pallets/cf-witnesser/src/lib.rs +++ b/state-chain/pallets/cf-witnesser/src/lib.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] +#![feature(extract_if)] #![doc = include_str!("../README.md")] #![doc = include_str!("../../cf-doc-head.md")] @@ -14,19 +15,49 @@ mod tests; use bitvec::prelude::*; use cf_primitives::EpochIndex; -use cf_traits::{impl_pallet_safe_mode, AccountRoleRegistry, Chainflip, EpochInfo}; +use cf_traits::{AccountRoleRegistry, CallDispatchFilter, Chainflip, EpochInfo, SafeMode}; use cf_utilities::success_threshold_from_share_count; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ dispatch::{DispatchResultWithPostInfo, GetDispatchInfo, UnfilteredDispatchable}, ensure, pallet_prelude::Member, storage::with_storage_layer, traits::{EnsureOrigin, Get}, - Hashable, + Hashable, RuntimeDebug, }; -use sp_std::{collections::vec_deque::VecDeque, prelude::*}; +use scale_info::TypeInfo; +use sp_std::prelude::*; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Copy, Clone, PartialEq, Eq, RuntimeDebug)] +pub enum PalletSafeMode { + CodeGreen, + CodeRed, + CodeAmber(CallPermission), +} -impl_pallet_safe_mode!(PalletSafeMode; witness_calls_enabled); +impl> CallDispatchFilter + for PalletSafeMode +{ + fn should_dispatch(&self, call: &C) -> bool { + match self { + Self::CodeGreen => true, + Self::CodeRed => false, + Self::CodeAmber(permissions) => permissions.should_dispatch(call), + } + } +} + +impl Default for PalletSafeMode { + fn default() -> Self { + as SafeMode>::CODE_GREEN + } +} + +impl SafeMode for PalletSafeMode { + const CODE_RED: Self = PalletSafeMode::CodeRed; + const CODE_GREEN: Self = PalletSafeMode::CodeGreen; +} pub trait WitnessDataExtraction { /// Extracts some data from a call and encodes it so it can be stored for later. @@ -63,7 +94,10 @@ pub mod pallet { + WitnessDataExtraction; /// Safe Mode access. - type SafeMode: Get; + type SafeMode: Get>; + + /// Filter for dispatching witnessed calls. + type CallDispatchPermission: Parameter + CallDispatchFilter<::RuntimeCall>; /// Benchmark stuff type WeightInfo: WeightInfo; @@ -112,44 +146,36 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { - /// Clear stale data from expired epochs fn on_idle(_block_number: BlockNumberFor, remaining_weight: Weight) -> Weight { let mut used_weight = Weight::zero(); - if T::SafeMode::get().witness_calls_enabled { - let _ = WitnessedCallsScheduledForDispatch::::try_mutate( - |witnessed_calls_storage| { - used_weight.saturating_accrue(T::DbWeight::get().reads(1)); - if !witnessed_calls_storage.is_empty() { - let mut witnessed_calls = - VecDeque::from(sp_std::mem::take(witnessed_calls_storage)); - while let Some((_, call, _)) = witnessed_calls.front() { - let next_weight = - used_weight.saturating_add(call.get_dispatch_info().weight); - if remaining_weight.all_gte(next_weight) { - used_weight = next_weight; - let (witnessed_at_epoch, call, call_hash) = - witnessed_calls.pop_front().unwrap(); - Self::dispatch_call( - witnessed_at_epoch, - T::EpochInfo::epoch_index(), - call, - call_hash, - ); - } else { - break - } + + let safe_mode = T::SafeMode::get(); + if safe_mode != SafeMode::CODE_RED { + WitnessedCallsScheduledForDispatch::::mutate(|witnessed_calls_storage| { + witnessed_calls_storage + .extract_if(|(_, call, _)| { + let next_weight = + used_weight.saturating_add(call.get_dispatch_info().weight); + if remaining_weight.all_gte(next_weight) && + safe_mode.should_dispatch(call) + { + used_weight = next_weight; + true + } else { + false } - let _empty = sp_std::mem::replace( - witnessed_calls_storage, - witnessed_calls.make_contiguous().to_vec(), + }) + .collect::>() + .into_iter() + .for_each(|(witnessed_at_epoch, call, call_hash)| { + Self::dispatch_call( + witnessed_at_epoch, + T::EpochInfo::epoch_index(), + call, + call_hash, ); - used_weight.saturating_accrue(T::DbWeight::get().writes(1)); - Ok(()) - } else { - Err("no action needed when the scheduled witness calls list is empty") - } - }, - ); + }); + }); } let mut epochs_to_cull = EpochsToCull::::get(); @@ -352,7 +378,7 @@ pub mod pallet { if let Some(mut extra_data) = ExtraCallData::::get(epoch_index, call_hash) { call.combine_and_inject(&mut extra_data) } - if T::SafeMode::get().witness_calls_enabled { + if T::SafeMode::get().should_dispatch(&call) { Self::dispatch_call(epoch_index, current_epoch, *call, call_hash); } else { WitnessedCallsScheduledForDispatch::::append(( diff --git a/state-chain/pallets/cf-witnesser/src/mock.rs b/state-chain/pallets/cf-witnesser/src/mock.rs index 7542e8c323..f2710e9d9f 100644 --- a/state-chain/pallets/cf-witnesser/src/mock.rs +++ b/state-chain/pallets/cf-witnesser/src/mock.rs @@ -1,9 +1,13 @@ #![cfg(test)] use crate::{self as pallet_cf_witness, WitnessDataExtraction}; -use cf_traits::{impl_mock_chainflip, impl_mock_runtime_safe_mode, AccountRoleRegistry}; -use frame_support::parameter_types; +use cf_traits::{ + impl_mock_chainflip, impl_mock_runtime_safe_mode, AccountRoleRegistry, CallDispatchFilter, +}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{parameter_types, RuntimeDebug}; use frame_system as system; +use scale_info::TypeInfo; use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use sp_std::collections::btree_set::BTreeSet; @@ -53,13 +57,27 @@ impl system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<5>; } -impl_mock_runtime_safe_mode! { witnesser: pallet_cf_witness::PalletSafeMode } +impl_mock_runtime_safe_mode! { witnesser: pallet_cf_witness::PalletSafeMode } + +parameter_types! { + pub static AllowCall: bool = true; +} + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Copy, Clone, PartialEq, Eq, RuntimeDebug)] +pub struct MockCallFilter; + +impl CallDispatchFilter for MockCallFilter { + fn should_dispatch(&self, _call: &RuntimeCall) -> bool { + AllowCall::get() + } +} impl pallet_cf_witness::Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type SafeMode = MockRuntimeSafeMode; + type CallDispatchPermission = MockCallFilter; type WeightInfo = (); } diff --git a/state-chain/pallets/cf-witnesser/src/tests.rs b/state-chain/pallets/cf-witnesser/src/tests.rs index 49e4592dc0..7fbc6bb947 100644 --- a/state-chain/pallets/cf-witnesser/src/tests.rs +++ b/state-chain/pallets/cf-witnesser/src/tests.rs @@ -389,3 +389,38 @@ fn test_safe_mode() { assert_eq!(pallet_dummy::Something::::get(), Some(0u32)); }); } + +#[test] +fn safe_mode_code_amber_can_filter_calls() { + new_test_ext().execute_with(|| { + // Block calls via SafeMode::CodeAmber + MockRuntimeSafeMode::set_safe_mode(MockRuntimeSafeMode { + witnesser: PalletSafeMode::CodeAmber(MockCallFilter {}), + }); + AllowCall::set(false); + + // Sign the call so its ready to be dispatched + let call = Box::new(RuntimeCall::Dummy(pallet_dummy::Call::::increment_value {})); + let current_epoch = MockEpochInfo::epoch_index(); + for s in [ALISSA, BOBSON] { + assert_ok!(Witnesser::witness_at_epoch( + RuntimeOrigin::signed(s), + call.clone(), + current_epoch + )); + } + assert_eq!(WitnessedCallsScheduledForDispatch::::decode_len(), Some(1)); + + // Call is not dispatched because its blocked by the CallDispatchFilter + Witnesser::on_idle(1, Weight::zero().set_ref_time(1_000_000_000_000u64)); + assert!(!WitnessedCallsScheduledForDispatch::::get().is_empty()); + + // Allow the call to pass the filter + AllowCall::set(true); + + // Call should be dispatched now. + Witnesser::on_idle(2, Weight::zero().set_ref_time(1_000_000_000_000u64)); + assert!(WitnessedCallsScheduledForDispatch::::get().is_empty()); + assert_eq!(pallet_dummy::Something::::get(), Some(0u32)); + }); +} diff --git a/state-chain/runtime/Cargo.toml b/state-chain/runtime/Cargo.toml index cc04345f82..38708ff8f3 100644 --- a/state-chain/runtime/Cargo.toml +++ b/state-chain/runtime/Cargo.toml @@ -29,6 +29,7 @@ cf-amm = { path = '../amm', default-features = false } cf-chains = { path = '../chains', default-features = false } cf-primitives = { path = '../primitives', default-features = false } cf-session-benchmarking = { path = '../cf-session-benchmarking', optional = true, default-features = false } +cf-runtime-utilities = { path = '../runtime-utilities', default-features = false } cf-traits = { path = '../traits', default-features = false } cf-utilities = { package = 'utilities', path = '../../utilities', default-features = false } @@ -134,6 +135,7 @@ std = [ 'cf-amm/std', 'cf-chains/std', 'cf-primitives/std', + 'cf-runtime-utilities/std', 'cf-traits/std', 'cf-utilities/std', 'codec/std', diff --git a/state-chain/runtime/src/chainflip.rs b/state-chain/runtime/src/chainflip.rs index 29ede49402..4bde2f0d4e 100644 --- a/state-chain/runtime/src/chainflip.rs +++ b/state-chain/runtime/src/chainflip.rs @@ -44,10 +44,10 @@ use cf_chains::{ }; use cf_primitives::{chains::assets, AccountRole, Asset, BasisPoints, ChannelId, EgressId}; use cf_traits::{ - impl_runtime_safe_mode, AccountRoleRegistry, BlockEmissions, BroadcastAnyChainGovKey, - Broadcaster, Chainflip, CommKeyBroadcaster, DepositApi, DepositHandler, EgressApi, EpochInfo, - Heartbeat, Issuance, KeyProvider, OnBroadcastReady, QualifyNode, RewardsDistribution, - RuntimeUpgrade, VaultTransitionHandler, + AccountRoleRegistry, BlockEmissions, BroadcastAnyChainGovKey, Broadcaster, Chainflip, + CommKeyBroadcaster, DepositApi, DepositHandler, EgressApi, EpochInfo, Heartbeat, Issuance, + KeyProvider, OnBroadcastReady, QualifyNode, RewardsDistribution, RuntimeUpgrade, + VaultTransitionHandler, }; use codec::{Decode, Encode}; use frame_support::{ @@ -76,21 +76,6 @@ impl Chainflip for Runtime { type AccountRoleRegistry = AccountRoles; type FundingInfo = Flip; } - -impl_runtime_safe_mode! { - RuntimeSafeMode, - pallet_cf_environment::RuntimeSafeMode, - emissions: pallet_cf_emissions::PalletSafeMode, - funding: pallet_cf_funding::PalletSafeMode, - swapping: pallet_cf_swapping::PalletSafeMode, - liquidity_provider: pallet_cf_lp::PalletSafeMode, - validator: pallet_cf_validator::PalletSafeMode, - pools: pallet_cf_pools::PalletSafeMode, - reputation: pallet_cf_reputation::PalletSafeMode, - vault: pallet_cf_vaults::PalletSafeMode, - witnesser: pallet_cf_witnesser::PalletSafeMode, - broadcast: pallet_cf_broadcast::PalletSafeMode, -} struct BackupNodeEmissions; impl RewardsDistribution for BackupNodeEmissions { diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index 2d8d3871fd..5c3be0658e 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -4,6 +4,7 @@ pub mod chainflip; pub mod constants; pub mod runtime_apis; +pub mod safe_mode; #[cfg(feature = "std")] pub mod test_runner; mod weights; @@ -84,12 +85,13 @@ pub use cf_traits::{EpochInfo, QualifyNode, SessionKeysRegistered, SwappingApi}; pub use chainflip::chain_instances::*; use chainflip::{ - epoch_transition::ChainflipEpochTransitions, BroadcastReadyProvider, BtcEnvironment, - BtcVaultTransitionHandler, ChainAddressConverter, ChainflipHeartbeat, EthEnvironment, + all_vaults_rotator::AllVaultRotator, epoch_transition::ChainflipEpochTransitions, + BroadcastReadyProvider, BtcEnvironment, BtcVaultTransitionHandler, ChainAddressConverter, + ChainflipHeartbeat, DotEnvironment, DotVaultTransitionHandler, EthEnvironment, EthVaultTransitionHandler, TokenholderGovernanceBroadcaster, }; +use safe_mode::{RuntimeSafeMode, WitnesserCallPermission}; -use chainflip::{all_vaults_rotator::AllVaultRotator, DotEnvironment, DotVaultTransitionHandler}; use constants::common::*; use pallet_cf_flip::{Bonder, FlipSlasher}; use pallet_cf_vaults::Vault; @@ -184,7 +186,7 @@ impl pallet_cf_validator::Config for Runtime { ); type OffenceReporter = Reputation; type Bonder = Bonder; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type ReputationResetter = Reputation; } @@ -215,7 +217,7 @@ impl pallet_cf_environment::Config for Runtime { type BitcoinVaultKeyWitnessedHandler = BitcoinVault; type BitcoinNetwork = BitcoinNetworkParam; type BitcoinFeeInfo = chainflip::BitcoinFeeGetter; - type RuntimeSafeMode = chainflip::RuntimeSafeMode; + type RuntimeSafeMode = RuntimeSafeMode; type CurrentCompatibilityVersion = CurrentCompatibilityVersion; type WeightInfo = pallet_cf_environment::weights::PalletWeight; } @@ -226,7 +228,7 @@ impl pallet_cf_swapping::Config for Runtime { type EgressHandler = chainflip::AnyChainIngressEgressHandler; type SwappingApi = LiquidityPools; type AddressConverter = ChainAddressConverter; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type WeightInfo = pallet_cf_swapping::weights::PalletWeight; } @@ -244,7 +246,7 @@ impl pallet_cf_vaults::Config for Runtime { type OffenceReporter = Reputation; type WeightInfo = pallet_cf_vaults::weights::PalletWeight; type ChainTracking = EthereumChainTracking; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type Slasher = FlipSlasher; } @@ -262,7 +264,7 @@ impl pallet_cf_vaults::Config for Runtime { type OffenceReporter = Reputation; type WeightInfo = pallet_cf_vaults::weights::PalletWeight; type ChainTracking = PolkadotChainTracking; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type Slasher = FlipSlasher; } @@ -280,7 +282,7 @@ impl pallet_cf_vaults::Config for Runtime { type OffenceReporter = Reputation; type WeightInfo = pallet_cf_vaults::weights::PalletWeight; type ChainTracking = BitcoinChainTracking; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type Slasher = FlipSlasher; } @@ -342,7 +344,7 @@ impl pallet_cf_pools::Config for Runtime { type RuntimeEvent = RuntimeEvent; type LpBalance = LiquidityProvider; type NetworkFee = NetworkFee; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type WeightInfo = (); } @@ -351,7 +353,7 @@ impl pallet_cf_lp::Config for Runtime { type DepositHandler = chainflip::AnyChainIngressEgressHandler; type EgressHandler = chainflip::AnyChainIngressEgressHandler; type AddressConverter = ChainAddressConverter; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type WeightInfo = pallet_cf_lp::weights::PalletWeight; } @@ -402,7 +404,6 @@ parameter_types! { } // Configure FRAME pallets to include in runtime. - impl frame_system::Config for Runtime { /// The basic call filter to use in dispatchable. type BaseCallFilter = frame_support::traits::Everything; @@ -529,7 +530,8 @@ impl pallet_cf_witnesser::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; + type CallDispatchPermission = WitnesserCallPermission; type WeightInfo = pallet_cf_witnesser::weights::PalletWeight; } @@ -543,7 +545,7 @@ impl pallet_cf_funding::Config for Runtime { pallet_cf_threshold_signature::EnsureThresholdSigned; type RegisterRedemption = EthereumApi; type TimeSource = Timestamp; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type WeightInfo = pallet_cf_funding::weights::PalletWeight; } @@ -583,7 +585,7 @@ impl pallet_cf_emissions::Config for Runtime { type EthEnvironment = EthEnvironment; type FlipToBurn = LiquidityPools; type EgressHandler = chainflip::AnyChainIngressEgressHandler; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type WeightInfo = pallet_cf_emissions::weights::PalletWeight; } @@ -615,7 +617,7 @@ impl pallet_cf_reputation::Config for Runtime { type Slasher = FlipSlasher; type WeightInfo = pallet_cf_reputation::weights::PalletWeight; type MaximumAccruableReputation = MaximumAccruableReputation; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; } impl pallet_cf_threshold_signature::Config for Runtime { @@ -677,7 +679,7 @@ impl pallet_cf_broadcast::Config for Runtime { type BroadcastReadyProvider = BroadcastReadyProvider; type BroadcastTimeout = ConstU32<{ 10 * MINUTES }>; type WeightInfo = pallet_cf_broadcast::weights::PalletWeight; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type SafeModeBlockMargin = ConstU32<10>; type KeyProvider = EthereumVault; } @@ -699,7 +701,7 @@ impl pallet_cf_broadcast::Config for Runtime { type BroadcastReadyProvider = BroadcastReadyProvider; type BroadcastTimeout = ConstU32<{ 10 * MINUTES }>; type WeightInfo = pallet_cf_broadcast::weights::PalletWeight; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type SafeModeBlockMargin = ConstU32<10>; type KeyProvider = PolkadotVault; } @@ -721,7 +723,7 @@ impl pallet_cf_broadcast::Config for Runtime { type BroadcastReadyProvider = BroadcastReadyProvider; type BroadcastTimeout = ConstU32<{ 90 * MINUTES }>; type WeightInfo = pallet_cf_broadcast::weights::PalletWeight; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type SafeModeBlockMargin = ConstU32<10>; type KeyProvider = BitcoinVault; } diff --git a/state-chain/runtime/src/safe_mode.rs b/state-chain/runtime/src/safe_mode.rs new file mode 100644 index 0000000000..f9a6a76cdd --- /dev/null +++ b/state-chain/runtime/src/safe_mode.rs @@ -0,0 +1,117 @@ +//! For filtering runtime calls and other related utilities. + +use crate::{Runtime, RuntimeCall}; +use cf_traits::{impl_runtime_safe_mode, CallDispatchFilter}; +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +impl_runtime_safe_mode! { + RuntimeSafeMode, + pallet_cf_environment::RuntimeSafeMode, + emissions: pallet_cf_emissions::PalletSafeMode, + funding: pallet_cf_funding::PalletSafeMode, + swapping: pallet_cf_swapping::PalletSafeMode, + liquidity_provider: pallet_cf_lp::PalletSafeMode, + validator: pallet_cf_validator::PalletSafeMode, + pools: pallet_cf_pools::PalletSafeMode, + reputation: pallet_cf_reputation::PalletSafeMode, + vault: pallet_cf_vaults::PalletSafeMode, + witnesser: pallet_cf_witnesser::PalletSafeMode, + broadcast: pallet_cf_broadcast::PalletSafeMode, +} + +/// Contains permissions for different Runtime calls. +/// This is done through the SafeMode::CodeAmber of the Witnesser pallet. +/// Only calls allowed here can be dispatched with Witnesser origin. +#[derive( + Encode, + Decode, + MaxEncodedLen, + TypeInfo, + Default, + Copy, + Clone, + PartialEq, + Eq, + frame_support::RuntimeDebug, +)] +pub struct WitnesserCallPermission { + // Non-instantiable pallets + pub governance: bool, + pub funding: bool, + pub swapping: bool, + + // Ethereum pallets + pub ethereum_broadcast: bool, + pub ethereum_chain_tracking: bool, + pub ethereum_ingress_egress: bool, + pub ethereum_vault: bool, + + // Polkadot pallets + pub polkadot_broadcast: bool, + pub polkadot_chain_tracking: bool, + pub polkadot_ingress_egress: bool, + pub polkadot_vault: bool, + + // Bitcoin pallets + pub bitcoin_broadcast: bool, + pub bitcoin_chain_tracking: bool, + pub bitcoin_ingress_egress: bool, + pub bitcoin_vault: bool, +} + +impl WitnesserCallPermission { + pub fn allow_all() -> Self { + WitnesserCallPermission { + governance: true, + funding: true, + swapping: true, + ethereum_broadcast: true, + ethereum_chain_tracking: true, + ethereum_ingress_egress: true, + ethereum_vault: true, + polkadot_broadcast: true, + polkadot_chain_tracking: true, + polkadot_ingress_egress: true, + polkadot_vault: true, + bitcoin_broadcast: true, + bitcoin_chain_tracking: true, + bitcoin_ingress_egress: true, + bitcoin_vault: true, + } + } +} + +impl CallDispatchFilter for WitnesserCallPermission { + fn should_dispatch(&self, call: &RuntimeCall) -> bool { + match call { + RuntimeCall::Governance(..) => self.governance, + RuntimeCall::Funding(..) => self.funding, + RuntimeCall::Swapping(..) => self.swapping, + + RuntimeCall::EthereumBroadcaster(..) => self.ethereum_broadcast, + RuntimeCall::EthereumChainTracking(..) => self.ethereum_chain_tracking, + RuntimeCall::EthereumIngressEgress(..) => self.ethereum_ingress_egress, + RuntimeCall::EthereumVault(..) => self.ethereum_vault, + + RuntimeCall::PolkadotBroadcaster(..) => self.polkadot_broadcast, + RuntimeCall::PolkadotChainTracking(..) => self.polkadot_chain_tracking, + RuntimeCall::PolkadotIngressEgress(..) => self.polkadot_ingress_egress, + RuntimeCall::PolkadotVault(..) => self.polkadot_vault, + + RuntimeCall::BitcoinBroadcaster(..) => self.bitcoin_broadcast, + RuntimeCall::BitcoinChainTracking(..) => self.bitcoin_chain_tracking, + RuntimeCall::BitcoinIngressEgress(..) => self.bitcoin_ingress_egress, + RuntimeCall::BitcoinVault(..) => self.bitcoin_vault, + + _ => { + cf_runtime_utilities::log_or_panic!( + "All witnesser calls must be controllable through `WitnesserCallPermission`. Call: {:?}", + call + ); + #[allow(unreachable_code)] + false + }, + } + } +} diff --git a/state-chain/traits/src/lib.rs b/state-chain/traits/src/lib.rs index 52f36cb51e..b87c948922 100644 --- a/state-chain/traits/src/lib.rs +++ b/state-chain/traits/src/lib.rs @@ -815,3 +815,13 @@ pub trait AuthoritiesCfeVersions { /// Returns the percentage of current authorities with their CFEs at the given version. fn precent_authorities_at_version(version: SemVer) -> Percent; } + +pub trait CallDispatchFilter { + fn should_dispatch(&self, call: &RuntimeCall) -> bool; +} + +impl CallDispatchFilter for () { + fn should_dispatch(&self, _call: &RuntimeCall) -> bool { + true + } +}