From 1b561e4baa90814c106bb54bc6d0ce9711db1e36 Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Thu, 14 Sep 2023 17:59:40 +1200 Subject: [PATCH 1/6] Added a call filter as part of safe mode for the witnesser pallet. Only calls that is allowed by the filter will be dispatched. --- state-chain/pallets/cf-witnesser/src/lib.rs | 118 +++++++++++++------ state-chain/pallets/cf-witnesser/src/mock.rs | 15 ++- state-chain/runtime/src/chainflip.rs | 32 ++++- state-chain/runtime/src/lib.rs | 6 +- state-chain/traits/src/lib.rs | 10 ++ 5 files changed, 137 insertions(+), 44 deletions(-) diff --git a/state-chain/pallets/cf-witnesser/src/lib.rs b/state-chain/pallets/cf-witnesser/src/lib.rs index c2b9c9d7d7..2a014c7af1 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,7 +15,7 @@ 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 frame_support::{ dispatch::{DispatchResultWithPostInfo, GetDispatchInfo, UnfilteredDispatchable}, @@ -24,9 +25,58 @@ use frame_support::{ traits::{EnsureOrigin, Get}, Hashable, }; -use sp_std::{collections::vec_deque::VecDeque, prelude::*}; +use sp_std::prelude::*; + +/// Contains permissions for different Runtime calls. Only calls allowed here can be dispatched +/// with Witnesser origin. +#[derive( + codec::Encode, + codec::Decode, + codec::MaxEncodedLen, + scale_info::TypeInfo, + Copy, + Clone, + PartialEq, + Eq, + frame_support::RuntimeDebug, +)] +pub struct WitnesserCallPermission { + pub system: bool, // system, sessions, timestamp etc. + pub swappings: bool, // Swapping, pools, etc. + pub liquidity: bool, // LP + pub rotation: bool, // Validator, Vaults + pub ingress_egress: bool, // for transfering funds in and out of the chain. + pub broadcast: bool, // for broadcasting messages to other chains. + pub others: bool, // All other pallets +} + +#[derive( + codec::Encode, + codec::Decode, + codec::MaxEncodedLen, + scale_info::TypeInfo, + Copy, + Clone, + PartialEq, + Eq, + frame_support::RuntimeDebug, +)] +pub enum PalletSafeMode { + CodeGreen, + CodeRed, + CodeAmber(WitnesserCallPermission), +} -impl_pallet_safe_mode!(PalletSafeMode; witness_calls_enabled); +impl Default for PalletSafeMode { + fn default() -> Self { + ::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. @@ -65,6 +115,9 @@ pub mod pallet { /// Safe Mode access. type SafeMode: Get; + /// Filter for dispatching witnessed calls. + type CallDispatchFilter: CallDispatchFilter<::RuntimeCall>; + /// Benchmark stuff type WeightInfo: WeightInfo; } @@ -115,41 +168,32 @@ pub mod 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 - } + if T::SafeMode::get() != 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) && + T::CallDispatchFilter::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 +396,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::CallDispatchFilter::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..d0a5d0a504 100644 --- a/state-chain/pallets/cf-witnesser/src/mock.rs +++ b/state-chain/pallets/cf-witnesser/src/mock.rs @@ -1,7 +1,9 @@ #![cfg(test)] use crate::{self as pallet_cf_witness, WitnessDataExtraction}; -use cf_traits::{impl_mock_chainflip, impl_mock_runtime_safe_mode, AccountRoleRegistry}; +use cf_traits::{ + impl_mock_chainflip, impl_mock_runtime_safe_mode, AccountRoleRegistry, CallDispatchFilter, +}; use frame_support::parameter_types; use frame_system as system; use sp_core::H256; @@ -55,11 +57,22 @@ impl system::Config for Test { impl_mock_runtime_safe_mode! { witnesser: pallet_cf_witness::PalletSafeMode } +pub struct MockCallFilter; +impl CallDispatchFilter for MockCallFilter { + fn should_dispatch(_call: &RuntimeCall) -> bool { + match MockSafeModeStorage::get().witnesser { + pallet_cf_witness::PalletSafeMode::CodeGreen => true, + _ => false, + } + } +} + impl pallet_cf_witness::Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type SafeMode = MockRuntimeSafeMode; + type CallDispatchFilter = MockCallFilter; type WeightInfo = (); } diff --git a/state-chain/runtime/src/chainflip.rs b/state-chain/runtime/src/chainflip.rs index fd4ed6ca41..dd8bee022c 100644 --- a/state-chain/runtime/src/chainflip.rs +++ b/state-chain/runtime/src/chainflip.rs @@ -45,9 +45,9 @@ 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, + Broadcaster, CallDispatchFilter, Chainflip, CommKeyBroadcaster, DepositApi, DepositHandler, + EgressApi, EpochInfo, Heartbeat, Issuance, KeyProvider, OnBroadcastReady, QualifyNode, + RewardsDistribution, RuntimeUpgrade, VaultTransitionHandler, }; use codec::{Decode, Encode}; use frame_support::{ @@ -623,3 +623,29 @@ impl QualifyNode<::ValidatorId> for ValidatorRoleQualifica AccountRoles::has_account_role(id, AccountRole::Validator) } } + +pub struct ChainflipCallFilter; +impl CallDispatchFilter for ChainflipCallFilter { + fn should_dispatch(call: &RuntimeCall) -> bool { + match >::get() { + pallet_cf_witnesser::PalletSafeMode::CodeGreen => true, + pallet_cf_witnesser::PalletSafeMode::CodeRed => false, + pallet_cf_witnesser::PalletSafeMode::CodeAmber(permission) => match call { + RuntimeCall::System(..) | RuntimeCall::Timestamp(..) => permission.system, + RuntimeCall::Swapping(..) | RuntimeCall::LiquidityPools(..) => permission.swappings, + RuntimeCall::LiquidityProvider(..) => permission.liquidity, + RuntimeCall::Validator(..) | + RuntimeCall::EthereumVault(..) | + RuntimeCall::PolkadotVault(..) | + RuntimeCall::BitcoinVault(..) => permission.rotation, + RuntimeCall::EthereumIngressEgress(..) | + RuntimeCall::PolkadotIngressEgress(..) | + RuntimeCall::BitcoinIngressEgress(..) => permission.ingress_egress, + RuntimeCall::EthereumBroadcaster(..) | + RuntimeCall::PolkadotBroadcaster(..) | + RuntimeCall::BitcoinBroadcaster(..) => permission.broadcast, + _ => permission.others, + }, + } + } +} diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index e0033c48b5..9c5e762164 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -85,8 +85,8 @@ pub use cf_traits::{EpochInfo, QualifyNode, SessionKeysRegistered, SwappingApi}; pub use chainflip::chain_instances::*; use chainflip::{ epoch_transition::ChainflipEpochTransitions, BroadcastReadyProvider, BtcEnvironment, - BtcVaultTransitionHandler, ChainAddressConverter, ChainflipHeartbeat, EthEnvironment, - EthVaultTransitionHandler, TokenholderGovernanceBroadcaster, + BtcVaultTransitionHandler, ChainAddressConverter, ChainflipCallFilter, ChainflipHeartbeat, + EthEnvironment, EthVaultTransitionHandler, TokenholderGovernanceBroadcaster, }; use chainflip::{all_vaults_rotator::AllVaultRotator, DotEnvironment, DotVaultTransitionHandler}; @@ -393,7 +393,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; @@ -521,6 +520,7 @@ impl pallet_cf_witnesser::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type SafeMode = chainflip::RuntimeSafeMode; + type CallDispatchFilter = ChainflipCallFilter; type WeightInfo = pallet_cf_witnesser::weights::PalletWeight; } diff --git a/state-chain/traits/src/lib.rs b/state-chain/traits/src/lib.rs index 37280bbabc..43da1300d5 100644 --- a/state-chain/traits/src/lib.rs +++ b/state-chain/traits/src/lib.rs @@ -824,3 +824,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(call: &RuntimeCall) -> bool; +} + +impl CallDispatchFilter for () { + fn should_dispatch(_call: &RuntimeCall) -> bool { + true + } +} From 24aba2dffb054c59b3b4db4c48204ce7588be0af Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Thu, 14 Sep 2023 18:01:12 +1200 Subject: [PATCH 2/6] clippy fix --- state-chain/pallets/cf-witnesser/src/mock.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/state-chain/pallets/cf-witnesser/src/mock.rs b/state-chain/pallets/cf-witnesser/src/mock.rs index d0a5d0a504..6ddad666ca 100644 --- a/state-chain/pallets/cf-witnesser/src/mock.rs +++ b/state-chain/pallets/cf-witnesser/src/mock.rs @@ -60,10 +60,7 @@ impl_mock_runtime_safe_mode! { witnesser: pallet_cf_witness::PalletSafeMode } pub struct MockCallFilter; impl CallDispatchFilter for MockCallFilter { fn should_dispatch(_call: &RuntimeCall) -> bool { - match MockSafeModeStorage::get().witnesser { - pallet_cf_witness::PalletSafeMode::CodeGreen => true, - _ => false, - } + matches!(MockSafeModeStorage::get().witnesser, pallet_cf_witness::PalletSafeMode::CodeGreen) } } From aabb9bf2abe2091f4b663dee0e417700ff65f758 Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Fri, 15 Sep 2023 15:10:44 +1200 Subject: [PATCH 3/6] Improved how permissions are defined. Added a unit test for the call filter system. --- state-chain/pallets/cf-witnesser/src/lib.rs | 38 ++----- state-chain/pallets/cf-witnesser/src/mock.rs | 12 +- state-chain/pallets/cf-witnesser/src/tests.rs | 35 ++++++ state-chain/runtime/src/chainflip.rs | 106 +++++++++++++++--- state-chain/runtime/src/lib.rs | 7 +- 5 files changed, 148 insertions(+), 50 deletions(-) diff --git a/state-chain/pallets/cf-witnesser/src/lib.rs b/state-chain/pallets/cf-witnesser/src/lib.rs index 2a014c7af1..8a9463a3f7 100644 --- a/state-chain/pallets/cf-witnesser/src/lib.rs +++ b/state-chain/pallets/cf-witnesser/src/lib.rs @@ -27,8 +27,6 @@ use frame_support::{ }; use sp_std::prelude::*; -/// Contains permissions for different Runtime calls. Only calls allowed here can be dispatched -/// with Witnesser origin. #[derive( codec::Encode, codec::Decode, @@ -40,40 +38,19 @@ use sp_std::prelude::*; Eq, frame_support::RuntimeDebug, )] -pub struct WitnesserCallPermission { - pub system: bool, // system, sessions, timestamp etc. - pub swappings: bool, // Swapping, pools, etc. - pub liquidity: bool, // LP - pub rotation: bool, // Validator, Vaults - pub ingress_egress: bool, // for transfering funds in and out of the chain. - pub broadcast: bool, // for broadcasting messages to other chains. - pub others: bool, // All other pallets -} - -#[derive( - codec::Encode, - codec::Decode, - codec::MaxEncodedLen, - scale_info::TypeInfo, - Copy, - Clone, - PartialEq, - Eq, - frame_support::RuntimeDebug, -)] -pub enum PalletSafeMode { +pub enum PalletSafeMode { CodeGreen, CodeRed, - CodeAmber(WitnesserCallPermission), + CodeAmber(CallPermission), } -impl Default for PalletSafeMode { +impl Default for PalletSafeMode { fn default() -> Self { - ::CODE_GREEN + as SafeMode>::CODE_GREEN } } -impl SafeMode for PalletSafeMode { +impl SafeMode for PalletSafeMode { const CODE_RED: Self = PalletSafeMode::CodeRed; const CODE_GREEN: Self = PalletSafeMode::CodeGreen; } @@ -112,8 +89,11 @@ pub mod pallet { + GetDispatchInfo + WitnessDataExtraction; + /// A struct that contains permissions for different calls. + type CallPermission: Parameter; + /// Safe Mode access. - type SafeMode: Get; + type SafeMode: Get>; /// Filter for dispatching witnessed calls. type CallDispatchFilter: CallDispatchFilter<::RuntimeCall>; diff --git a/state-chain/pallets/cf-witnesser/src/mock.rs b/state-chain/pallets/cf-witnesser/src/mock.rs index 6ddad666ca..bae1d24ca2 100644 --- a/state-chain/pallets/cf-witnesser/src/mock.rs +++ b/state-chain/pallets/cf-witnesser/src/mock.rs @@ -55,12 +55,19 @@ 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; +} pub struct MockCallFilter; impl CallDispatchFilter for MockCallFilter { fn should_dispatch(_call: &RuntimeCall) -> bool { - matches!(MockSafeModeStorage::get().witnesser, pallet_cf_witness::PalletSafeMode::CodeGreen) + match MockSafeModeStorage::get().witnesser { + pallet_cf_witness::PalletSafeMode::CodeGreen => true, + pallet_cf_witness::PalletSafeMode::CodeRed => false, + pallet_cf_witness::PalletSafeMode::CodeAmber(()) => AllowCall::get(), + } } } @@ -69,6 +76,7 @@ impl pallet_cf_witness::Config for Test { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type SafeMode = MockRuntimeSafeMode; + type CallPermission = (); type CallDispatchFilter = 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..5f79ee8a68 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(()), + }); + 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/src/chainflip.rs b/state-chain/runtime/src/chainflip.rs index dd8bee022c..a5870594b5 100644 --- a/state-chain/runtime/src/chainflip.rs +++ b/state-chain/runtime/src/chainflip.rs @@ -85,7 +85,7 @@ impl_runtime_safe_mode! { pools: pallet_cf_pools::PalletSafeMode, reputation: pallet_cf_reputation::PalletSafeMode, vault: pallet_cf_vaults::PalletSafeMode, - witnesser: pallet_cf_witnesser::PalletSafeMode, + witnesser: pallet_cf_witnesser::PalletSafeMode, broadcast: pallet_cf_broadcast::PalletSafeMode, } struct BackupNodeEmissions; @@ -624,27 +624,101 @@ impl QualifyNode<::ValidatorId> for ValidatorRoleQualifica } } +/// 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( + codec::Encode, + codec::Decode, + codec::MaxEncodedLen, + scale_info::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, + } + } +} + pub struct ChainflipCallFilter; impl CallDispatchFilter for ChainflipCallFilter { fn should_dispatch(call: &RuntimeCall) -> bool { - match >::get() { + match , + >>::get() + { pallet_cf_witnesser::PalletSafeMode::CodeGreen => true, pallet_cf_witnesser::PalletSafeMode::CodeRed => false, pallet_cf_witnesser::PalletSafeMode::CodeAmber(permission) => match call { - RuntimeCall::System(..) | RuntimeCall::Timestamp(..) => permission.system, - RuntimeCall::Swapping(..) | RuntimeCall::LiquidityPools(..) => permission.swappings, - RuntimeCall::LiquidityProvider(..) => permission.liquidity, - RuntimeCall::Validator(..) | - RuntimeCall::EthereumVault(..) | - RuntimeCall::PolkadotVault(..) | - RuntimeCall::BitcoinVault(..) => permission.rotation, - RuntimeCall::EthereumIngressEgress(..) | - RuntimeCall::PolkadotIngressEgress(..) | - RuntimeCall::BitcoinIngressEgress(..) => permission.ingress_egress, - RuntimeCall::EthereumBroadcaster(..) | - RuntimeCall::PolkadotBroadcaster(..) | - RuntimeCall::BitcoinBroadcaster(..) => permission.broadcast, - _ => permission.others, + RuntimeCall::Governance(..) => permission.governance, + RuntimeCall::Funding(..) => permission.funding, + RuntimeCall::Swapping(..) => permission.swapping, + + RuntimeCall::EthereumBroadcaster(..) => permission.ethereum_broadcast, + RuntimeCall::EthereumChainTracking(..) => permission.ethereum_chain_tracking, + RuntimeCall::EthereumIngressEgress(..) => permission.ethereum_ingress_egress, + RuntimeCall::EthereumVault(..) => permission.ethereum_vault, + + RuntimeCall::PolkadotBroadcaster(..) => permission.polkadot_broadcast, + RuntimeCall::PolkadotChainTracking(..) => permission.polkadot_chain_tracking, + RuntimeCall::PolkadotIngressEgress(..) => permission.polkadot_ingress_egress, + RuntimeCall::PolkadotVault(..) => permission.polkadot_vault, + + RuntimeCall::BitcoinBroadcaster(..) => permission.bitcoin_broadcast, + RuntimeCall::BitcoinChainTracking(..) => permission.bitcoin_chain_tracking, + RuntimeCall::BitcoinIngressEgress(..) => permission.bitcoin_ingress_egress, + RuntimeCall::BitcoinVault(..) => permission.bitcoin_vault, + + _ => { + log::warn!("All witnesser calls must be controllable through `WitnesserCallPermission` during SafeMode: CodeAmber. Call: {:?}", call); + false + }, }, } } diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index 9c5e762164..79b6cfaf6c 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -84,12 +84,12 @@ pub use cf_traits::{EpochInfo, QualifyNode, SessionKeysRegistered, SwappingApi}; pub use chainflip::chain_instances::*; use chainflip::{ - epoch_transition::ChainflipEpochTransitions, BroadcastReadyProvider, BtcEnvironment, - BtcVaultTransitionHandler, ChainAddressConverter, ChainflipCallFilter, ChainflipHeartbeat, + all_vaults_rotator::AllVaultRotator, epoch_transition::ChainflipEpochTransitions, + BroadcastReadyProvider, BtcEnvironment, BtcVaultTransitionHandler, ChainAddressConverter, + ChainflipCallFilter, ChainflipHeartbeat, DotEnvironment, DotVaultTransitionHandler, EthEnvironment, EthVaultTransitionHandler, TokenholderGovernanceBroadcaster, }; -use chainflip::{all_vaults_rotator::AllVaultRotator, DotEnvironment, DotVaultTransitionHandler}; use constants::common::*; use pallet_cf_flip::{Bonder, FlipSlasher}; use pallet_cf_vaults::Vault; @@ -520,6 +520,7 @@ impl pallet_cf_witnesser::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type SafeMode = chainflip::RuntimeSafeMode; + type CallPermission = chainflip::WitnesserCallPermission; type CallDispatchFilter = ChainflipCallFilter; type WeightInfo = pallet_cf_witnesser::weights::PalletWeight; } From d0bf77c9ecec0a856848f3d5f551764c3aa8291e Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Mon, 18 Sep 2023 11:02:08 +1200 Subject: [PATCH 4/6] Moved safemode related things into its own file to improve chainflip.rs bloat. --- .../cf-integration-tests/src/authorities.rs | 2 +- state-chain/runtime/src/chainflip.rs | 123 +----------------- state-chain/runtime/src/lib.rs | 38 +++--- state-chain/runtime/src/safe_mode.rs | 122 +++++++++++++++++ 4 files changed, 147 insertions(+), 138 deletions(-) create mode 100644 state-chain/runtime/src/safe_mode.rs diff --git a/state-chain/cf-integration-tests/src/authorities.rs b/state-chain/cf-integration-tests/src/authorities.rs index 8c4b43df33..fd3e46844f 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, Environment, EthereumVault, Flip, Runtime, Validator, + safe_mode::RuntimeSafeMode, Environment, EthereumVault, Flip, Runtime, Validator, }; // Helper function that creates a network, funds backup nodes, and have them join the auction. diff --git a/state-chain/runtime/src/chainflip.rs b/state-chain/runtime/src/chainflip.rs index a5870594b5..da72bffb9a 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, CallDispatchFilter, 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::{ @@ -73,21 +73,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 { @@ -623,103 +608,3 @@ impl QualifyNode<::ValidatorId> for ValidatorRoleQualifica AccountRoles::has_account_role(id, AccountRole::Validator) } } - -/// 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( - codec::Encode, - codec::Decode, - codec::MaxEncodedLen, - scale_info::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, - } - } -} - -pub struct ChainflipCallFilter; -impl CallDispatchFilter for ChainflipCallFilter { - fn should_dispatch(call: &RuntimeCall) -> bool { - match , - >>::get() - { - pallet_cf_witnesser::PalletSafeMode::CodeGreen => true, - pallet_cf_witnesser::PalletSafeMode::CodeRed => false, - pallet_cf_witnesser::PalletSafeMode::CodeAmber(permission) => match call { - RuntimeCall::Governance(..) => permission.governance, - RuntimeCall::Funding(..) => permission.funding, - RuntimeCall::Swapping(..) => permission.swapping, - - RuntimeCall::EthereumBroadcaster(..) => permission.ethereum_broadcast, - RuntimeCall::EthereumChainTracking(..) => permission.ethereum_chain_tracking, - RuntimeCall::EthereumIngressEgress(..) => permission.ethereum_ingress_egress, - RuntimeCall::EthereumVault(..) => permission.ethereum_vault, - - RuntimeCall::PolkadotBroadcaster(..) => permission.polkadot_broadcast, - RuntimeCall::PolkadotChainTracking(..) => permission.polkadot_chain_tracking, - RuntimeCall::PolkadotIngressEgress(..) => permission.polkadot_ingress_egress, - RuntimeCall::PolkadotVault(..) => permission.polkadot_vault, - - RuntimeCall::BitcoinBroadcaster(..) => permission.bitcoin_broadcast, - RuntimeCall::BitcoinChainTracking(..) => permission.bitcoin_chain_tracking, - RuntimeCall::BitcoinIngressEgress(..) => permission.bitcoin_ingress_egress, - RuntimeCall::BitcoinVault(..) => permission.bitcoin_vault, - - _ => { - log::warn!("All witnesser calls must be controllable through `WitnesserCallPermission` during SafeMode: CodeAmber. Call: {:?}", call); - false - }, - }, - } - } -} diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index 79b6cfaf6c..0f0996c70f 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; @@ -86,9 +87,10 @@ pub use chainflip::chain_instances::*; use chainflip::{ all_vaults_rotator::AllVaultRotator, epoch_transition::ChainflipEpochTransitions, BroadcastReadyProvider, BtcEnvironment, BtcVaultTransitionHandler, ChainAddressConverter, - ChainflipCallFilter, ChainflipHeartbeat, DotEnvironment, DotVaultTransitionHandler, - EthEnvironment, EthVaultTransitionHandler, TokenholderGovernanceBroadcaster, + ChainflipHeartbeat, DotEnvironment, DotVaultTransitionHandler, EthEnvironment, + EthVaultTransitionHandler, TokenholderGovernanceBroadcaster, }; +use safe_mode::{ChainflipCallFilter, RuntimeSafeMode, WitnesserCallPermission}; use constants::common::*; use pallet_cf_flip::{Bonder, FlipSlasher}; @@ -175,7 +177,7 @@ impl pallet_cf_validator::Config for Runtime { ); type OffenceReporter = Reputation; type Bonder = Bonder; - type SafeMode = chainflip::RuntimeSafeMode; + type SafeMode = RuntimeSafeMode; type ReputationResetter = Reputation; } @@ -206,7 +208,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; } @@ -217,7 +219,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; } @@ -235,7 +237,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; } @@ -253,7 +255,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; } @@ -271,7 +273,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; } @@ -333,7 +335,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 = (); } @@ -342,7 +344,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; } @@ -519,8 +521,8 @@ impl pallet_cf_witnesser::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; - type SafeMode = chainflip::RuntimeSafeMode; - type CallPermission = chainflip::WitnesserCallPermission; + type SafeMode = RuntimeSafeMode; + type CallPermission = WitnesserCallPermission; type CallDispatchFilter = ChainflipCallFilter; type WeightInfo = pallet_cf_witnesser::weights::PalletWeight; } @@ -535,7 +537,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; } @@ -575,7 +577,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; } @@ -607,7 +609,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 { @@ -669,7 +671,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; } @@ -691,7 +693,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; } @@ -713,7 +715,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..0e8114f5c7 --- /dev/null +++ b/state-chain/runtime/src/safe_mode.rs @@ -0,0 +1,122 @@ +//! 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 frame_support::traits::Get; +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, + } + } +} + +pub struct ChainflipCallFilter; +impl CallDispatchFilter for ChainflipCallFilter { + fn should_dispatch(call: &RuntimeCall) -> bool { + match , + >>::get() + { + pallet_cf_witnesser::PalletSafeMode::CodeGreen => true, + pallet_cf_witnesser::PalletSafeMode::CodeRed => false, + pallet_cf_witnesser::PalletSafeMode::CodeAmber(permission) => match call { + RuntimeCall::Governance(..) => permission.governance, + RuntimeCall::Funding(..) => permission.funding, + RuntimeCall::Swapping(..) => permission.swapping, + + RuntimeCall::EthereumBroadcaster(..) => permission.ethereum_broadcast, + RuntimeCall::EthereumChainTracking(..) => permission.ethereum_chain_tracking, + RuntimeCall::EthereumIngressEgress(..) => permission.ethereum_ingress_egress, + RuntimeCall::EthereumVault(..) => permission.ethereum_vault, + + RuntimeCall::PolkadotBroadcaster(..) => permission.polkadot_broadcast, + RuntimeCall::PolkadotChainTracking(..) => permission.polkadot_chain_tracking, + RuntimeCall::PolkadotIngressEgress(..) => permission.polkadot_ingress_egress, + RuntimeCall::PolkadotVault(..) => permission.polkadot_vault, + + RuntimeCall::BitcoinBroadcaster(..) => permission.bitcoin_broadcast, + RuntimeCall::BitcoinChainTracking(..) => permission.bitcoin_chain_tracking, + RuntimeCall::BitcoinIngressEgress(..) => permission.bitcoin_ingress_egress, + RuntimeCall::BitcoinVault(..) => permission.bitcoin_vault, + + _ => { + log::warn!("All witnesser calls must be controllable through `WitnesserCallPermission` during SafeMode: CodeAmber. Call: {:?}", call); + false + }, + }, + } + } +} From fead8f38357d0da594d6c773b60ed401ed585033 Mon Sep 17 00:00:00 2001 From: Roy Yang Date: Wed, 20 Sep 2023 11:19:01 +1200 Subject: [PATCH 5/6] Combined CallDispatchFilter with CallPermission to reduce redundency. --- state-chain/pallets/cf-witnesser/src/lib.rs | 27 ++++++------------- state-chain/pallets/cf-witnesser/src/mock.rs | 13 +++++---- state-chain/pallets/cf-witnesser/src/tests.rs | 2 +- state-chain/runtime/src/lib.rs | 5 ++-- state-chain/runtime/src/safe_mode.rs | 3 +-- 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/state-chain/pallets/cf-witnesser/src/lib.rs b/state-chain/pallets/cf-witnesser/src/lib.rs index 8a9463a3f7..8c37e6f82c 100644 --- a/state-chain/pallets/cf-witnesser/src/lib.rs +++ b/state-chain/pallets/cf-witnesser/src/lib.rs @@ -17,27 +17,19 @@ use bitvec::prelude::*; use cf_primitives::EpochIndex; 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 scale_info::TypeInfo; use sp_std::prelude::*; -#[derive( - codec::Encode, - codec::Decode, - codec::MaxEncodedLen, - scale_info::TypeInfo, - Copy, - Clone, - PartialEq, - Eq, - frame_support::RuntimeDebug, -)] +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Copy, Clone, PartialEq, Eq, RuntimeDebug)] pub enum PalletSafeMode { CodeGreen, CodeRed, @@ -89,14 +81,11 @@ pub mod pallet { + GetDispatchInfo + WitnessDataExtraction; - /// A struct that contains permissions for different calls. - type CallPermission: Parameter; - /// Safe Mode access. - type SafeMode: Get>; + type SafeMode: Get>; /// Filter for dispatching witnessed calls. - type CallDispatchFilter: CallDispatchFilter<::RuntimeCall>; + type CallDispatchPermission: Parameter + CallDispatchFilter<::RuntimeCall>; /// Benchmark stuff type WeightInfo: WeightInfo; @@ -155,7 +144,7 @@ pub mod pallet { let next_weight = used_weight.saturating_add(call.get_dispatch_info().weight); if remaining_weight.all_gte(next_weight) && - T::CallDispatchFilter::should_dispatch(call) + T::CallDispatchPermission::should_dispatch(call) { used_weight = next_weight; true @@ -376,7 +365,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::CallDispatchFilter::should_dispatch(&call) { + if T::CallDispatchPermission::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 bae1d24ca2..50948677d3 100644 --- a/state-chain/pallets/cf-witnesser/src/mock.rs +++ b/state-chain/pallets/cf-witnesser/src/mock.rs @@ -4,8 +4,10 @@ use crate::{self as pallet_cf_witness, WitnessDataExtraction}; use cf_traits::{ impl_mock_chainflip, impl_mock_runtime_safe_mode, AccountRoleRegistry, CallDispatchFilter, }; -use frame_support::parameter_types; +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; @@ -55,18 +57,20 @@ 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(_call: &RuntimeCall) -> bool { match MockSafeModeStorage::get().witnesser { pallet_cf_witness::PalletSafeMode::CodeGreen => true, pallet_cf_witness::PalletSafeMode::CodeRed => false, - pallet_cf_witness::PalletSafeMode::CodeAmber(()) => AllowCall::get(), + pallet_cf_witness::PalletSafeMode::CodeAmber(MockCallFilter) => AllowCall::get(), } } } @@ -76,8 +80,7 @@ impl pallet_cf_witness::Config for Test { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type SafeMode = MockRuntimeSafeMode; - type CallPermission = (); - type CallDispatchFilter = MockCallFilter; + 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 5f79ee8a68..7fbc6bb947 100644 --- a/state-chain/pallets/cf-witnesser/src/tests.rs +++ b/state-chain/pallets/cf-witnesser/src/tests.rs @@ -395,7 +395,7 @@ 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(()), + witnesser: PalletSafeMode::CodeAmber(MockCallFilter {}), }); AllowCall::set(false); diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index 3c4072587a..5c3be0658e 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -90,7 +90,7 @@ use chainflip::{ ChainflipHeartbeat, DotEnvironment, DotVaultTransitionHandler, EthEnvironment, EthVaultTransitionHandler, TokenholderGovernanceBroadcaster, }; -use safe_mode::{ChainflipCallFilter, RuntimeSafeMode, WitnesserCallPermission}; +use safe_mode::{RuntimeSafeMode, WitnesserCallPermission}; use constants::common::*; use pallet_cf_flip::{Bonder, FlipSlasher}; @@ -531,8 +531,7 @@ impl pallet_cf_witnesser::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type SafeMode = RuntimeSafeMode; - type CallPermission = WitnesserCallPermission; - type CallDispatchFilter = ChainflipCallFilter; + type CallDispatchPermission = WitnesserCallPermission; type WeightInfo = pallet_cf_witnesser::weights::PalletWeight; } diff --git a/state-chain/runtime/src/safe_mode.rs b/state-chain/runtime/src/safe_mode.rs index 0e8114f5c7..035943b65c 100644 --- a/state-chain/runtime/src/safe_mode.rs +++ b/state-chain/runtime/src/safe_mode.rs @@ -83,8 +83,7 @@ impl WitnesserCallPermission { } } -pub struct ChainflipCallFilter; -impl CallDispatchFilter for ChainflipCallFilter { +impl CallDispatchFilter for WitnesserCallPermission { fn should_dispatch(call: &RuntimeCall) -> bool { match , From 0dc84072f29ff00f6d2817bb55adf81cdf98e2e2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 21 Sep 2023 14:55:31 +0200 Subject: [PATCH 6/6] chore: small refactor --- Cargo.lock | 1 + state-chain/pallets/cf-witnesser/src/lib.rs | 21 ++++++-- state-chain/pallets/cf-witnesser/src/mock.rs | 9 ++-- state-chain/runtime/Cargo.toml | 2 + state-chain/runtime/src/safe_mode.rs | 52 +++++++++----------- state-chain/traits/src/lib.rs | 4 +- 6 files changed, 49 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54240189a9..14df7309ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11425,6 +11425,7 @@ dependencies = [ "cf-amm", "cf-chains", "cf-primitives", + "cf-runtime-utilities", "cf-session-benchmarking", "cf-test-utilities", "cf-traits", diff --git a/state-chain/pallets/cf-witnesser/src/lib.rs b/state-chain/pallets/cf-witnesser/src/lib.rs index 8c37e6f82c..ae19005b03 100644 --- a/state-chain/pallets/cf-witnesser/src/lib.rs +++ b/state-chain/pallets/cf-witnesser/src/lib.rs @@ -36,6 +36,18 @@ pub enum PalletSafeMode { CodeAmber(CallPermission), } +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 @@ -134,17 +146,18 @@ 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() != SafeMode::CODE_RED { + + 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) && - T::CallDispatchPermission::should_dispatch(call) + safe_mode.should_dispatch(call) { used_weight = next_weight; true @@ -365,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::CallDispatchPermission::should_dispatch(&call) { + 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 50948677d3..f2710e9d9f 100644 --- a/state-chain/pallets/cf-witnesser/src/mock.rs +++ b/state-chain/pallets/cf-witnesser/src/mock.rs @@ -65,13 +65,10 @@ parameter_types! { #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Copy, Clone, PartialEq, Eq, RuntimeDebug)] pub struct MockCallFilter; + impl CallDispatchFilter for MockCallFilter { - fn should_dispatch(_call: &RuntimeCall) -> bool { - match MockSafeModeStorage::get().witnesser { - pallet_cf_witness::PalletSafeMode::CodeGreen => true, - pallet_cf_witness::PalletSafeMode::CodeRed => false, - pallet_cf_witness::PalletSafeMode::CodeAmber(MockCallFilter) => AllowCall::get(), - } + fn should_dispatch(&self, _call: &RuntimeCall) -> bool { + AllowCall::get() } } 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/safe_mode.rs b/state-chain/runtime/src/safe_mode.rs index 035943b65c..f9a6a76cdd 100644 --- a/state-chain/runtime/src/safe_mode.rs +++ b/state-chain/runtime/src/safe_mode.rs @@ -3,7 +3,6 @@ use crate::{Runtime, RuntimeCall}; use cf_traits::{impl_runtime_safe_mode, CallDispatchFilter}; use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::traits::Get; use scale_info::TypeInfo; impl_runtime_safe_mode! { @@ -84,37 +83,34 @@ impl WitnesserCallPermission { } impl CallDispatchFilter for WitnesserCallPermission { - fn should_dispatch(call: &RuntimeCall) -> bool { - match , - >>::get() - { - pallet_cf_witnesser::PalletSafeMode::CodeGreen => true, - pallet_cf_witnesser::PalletSafeMode::CodeRed => false, - pallet_cf_witnesser::PalletSafeMode::CodeAmber(permission) => match call { - RuntimeCall::Governance(..) => permission.governance, - RuntimeCall::Funding(..) => permission.funding, - RuntimeCall::Swapping(..) => permission.swapping, + fn should_dispatch(&self, call: &RuntimeCall) -> bool { + match call { + RuntimeCall::Governance(..) => self.governance, + RuntimeCall::Funding(..) => self.funding, + RuntimeCall::Swapping(..) => self.swapping, - RuntimeCall::EthereumBroadcaster(..) => permission.ethereum_broadcast, - RuntimeCall::EthereumChainTracking(..) => permission.ethereum_chain_tracking, - RuntimeCall::EthereumIngressEgress(..) => permission.ethereum_ingress_egress, - RuntimeCall::EthereumVault(..) => permission.ethereum_vault, + RuntimeCall::EthereumBroadcaster(..) => self.ethereum_broadcast, + RuntimeCall::EthereumChainTracking(..) => self.ethereum_chain_tracking, + RuntimeCall::EthereumIngressEgress(..) => self.ethereum_ingress_egress, + RuntimeCall::EthereumVault(..) => self.ethereum_vault, - RuntimeCall::PolkadotBroadcaster(..) => permission.polkadot_broadcast, - RuntimeCall::PolkadotChainTracking(..) => permission.polkadot_chain_tracking, - RuntimeCall::PolkadotIngressEgress(..) => permission.polkadot_ingress_egress, - RuntimeCall::PolkadotVault(..) => permission.polkadot_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(..) => permission.bitcoin_broadcast, - RuntimeCall::BitcoinChainTracking(..) => permission.bitcoin_chain_tracking, - RuntimeCall::BitcoinIngressEgress(..) => permission.bitcoin_ingress_egress, - RuntimeCall::BitcoinVault(..) => permission.bitcoin_vault, + RuntimeCall::BitcoinBroadcaster(..) => self.bitcoin_broadcast, + RuntimeCall::BitcoinChainTracking(..) => self.bitcoin_chain_tracking, + RuntimeCall::BitcoinIngressEgress(..) => self.bitcoin_ingress_egress, + RuntimeCall::BitcoinVault(..) => self.bitcoin_vault, - _ => { - log::warn!("All witnesser calls must be controllable through `WitnesserCallPermission` during SafeMode: CodeAmber. Call: {:?}", call); - false - }, + _ => { + 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 6de1f98dad..b87c948922 100644 --- a/state-chain/traits/src/lib.rs +++ b/state-chain/traits/src/lib.rs @@ -817,11 +817,11 @@ pub trait AuthoritiesCfeVersions { } pub trait CallDispatchFilter { - fn should_dispatch(call: &RuntimeCall) -> bool; + fn should_dispatch(&self, call: &RuntimeCall) -> bool; } impl CallDispatchFilter for () { - fn should_dispatch(_call: &RuntimeCall) -> bool { + fn should_dispatch(&self, _call: &RuntimeCall) -> bool { true } }