Skip to content

Commit

Permalink
Improved how permissions are defined.
Browse files Browse the repository at this point in the history
Added a unit test for the call filter system.
  • Loading branch information
syan095 committed Sep 15, 2023
1 parent 24aba2d commit aabb9bf
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 50 deletions.
38 changes: 9 additions & 29 deletions state-chain/pallets/cf-witnesser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<CallPermission> {
CodeGreen,
CodeRed,
CodeAmber(WitnesserCallPermission),
CodeAmber(CallPermission),
}

impl Default for PalletSafeMode {
impl<CallPermission> Default for PalletSafeMode<CallPermission> {
fn default() -> Self {
<PalletSafeMode as SafeMode>::CODE_GREEN
<PalletSafeMode<CallPermission> as SafeMode>::CODE_GREEN
}
}

impl SafeMode for PalletSafeMode {
impl<CallPermission> SafeMode for PalletSafeMode<CallPermission> {
const CODE_RED: Self = PalletSafeMode::CodeRed;
const CODE_GREEN: Self = PalletSafeMode::CodeGreen;
}
Expand Down Expand Up @@ -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<PalletSafeMode>;
type SafeMode: Get<PalletSafeMode<Self::CallPermission>>;

/// Filter for dispatching witnessed calls.
type CallDispatchFilter: CallDispatchFilter<<Self as Config>::RuntimeCall>;
Expand Down
12 changes: 10 additions & 2 deletions state-chain/pallets/cf-witnesser/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RuntimeCall> 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(),
}
}
}

Expand All @@ -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 = ();
}
Expand Down
35 changes: 35 additions & 0 deletions state-chain/pallets/cf-witnesser/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,38 @@ fn test_safe_mode() {
assert_eq!(pallet_dummy::Something::<Test>::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::<Test>::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::<Test>::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::<Test>::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::<Test>::get().is_empty());
assert_eq!(pallet_dummy::Something::<Test>::get(), Some(0u32));
});
}
106 changes: 90 additions & 16 deletions state-chain/runtime/src/chainflip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<WitnesserCallPermission>,
broadcast: pallet_cf_broadcast::PalletSafeMode,
}
struct BackupNodeEmissions;
Expand Down Expand Up @@ -624,27 +624,101 @@ impl QualifyNode<<Runtime as Chainflip>::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<RuntimeCall> for ChainflipCallFilter {
fn should_dispatch(call: &RuntimeCall) -> bool {
match <RuntimeSafeMode as Get<pallet_cf_witnesser::PalletSafeMode>>::get() {
match <RuntimeSafeMode as Get<
pallet_cf_witnesser::PalletSafeMode<WitnesserCallPermission>,
>>::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
},
},
}
}
Expand Down
7 changes: 4 additions & 3 deletions state-chain/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Runtime>;
}
Expand Down

0 comments on commit aabb9bf

Please sign in to comment.