Pre-dispatch call filter (#687)

* pre-dispatch call filter

* swap filter <-> weight

* clippy

* fmt
This commit is contained in:
Svyatoslav Nikolsky
2021-02-01 23:53:33 +03:00
committed by Bastian Köcher
parent a9e607c9a2
commit 8ee90afae6
3 changed files with 56 additions and 1 deletions
+1
View File
@@ -215,6 +215,7 @@ impl pallet_bridge_call_dispatch::Config for Runtime {
type Event = Event; type Event = Event;
type MessageId = (bp_message_lane::LaneId, bp_message_lane::MessageNonce); type MessageId = (bp_message_lane::LaneId, bp_message_lane::MessageNonce);
type Call = Call; type Call = Call;
type CallFilter = ();
type EncodedCall = crate::rialto_messages::FromRialtoEncodedCall; type EncodedCall = crate::rialto_messages::FromRialtoEncodedCall;
type SourceChainAccountId = bp_rialto::AccountId; type SourceChainAccountId = bp_rialto::AccountId;
type TargetChainAccountPublic = MultiSigner; type TargetChainAccountPublic = MultiSigner;
+1
View File
@@ -266,6 +266,7 @@ impl pallet_bridge_call_dispatch::Config for Runtime {
type Event = Event; type Event = Event;
type MessageId = (bp_message_lane::LaneId, bp_message_lane::MessageNonce); type MessageId = (bp_message_lane::LaneId, bp_message_lane::MessageNonce);
type Call = Call; type Call = Call;
type CallFilter = ();
type EncodedCall = crate::millau_messages::FromMillauEncodedCall; type EncodedCall = crate::millau_messages::FromMillauEncodedCall;
type SourceChainAccountId = bp_millau::AccountId; type SourceChainAccountId = bp_millau::AccountId;
type TargetChainAccountPublic = MultiSigner; type TargetChainAccountPublic = MultiSigner;
+54 -1
View File
@@ -31,7 +31,7 @@ use frame_support::{
decl_event, decl_module, decl_storage, decl_event, decl_module, decl_storage,
dispatch::{Dispatchable, Parameter}, dispatch::{Dispatchable, Parameter},
ensure, ensure,
traits::Get, traits::{Filter, Get},
weights::{extract_actual_weight, GetDispatchInfo}, weights::{extract_actual_weight, GetDispatchInfo},
RuntimeDebug, RuntimeDebug,
}; };
@@ -134,6 +134,11 @@ pub trait Config<I = DefaultInstance>: frame_system::Config {
Origin = <Self as frame_system::Config>::Origin, Origin = <Self as frame_system::Config>::Origin,
PostInfo = frame_support::dispatch::PostDispatchInfo, PostInfo = frame_support::dispatch::PostDispatchInfo,
>; >;
/// Pre-dispatch filter for incoming calls.
///
/// The pallet will filter all incoming calls right before they're dispatched. If this filter
/// rejects the call, special event (`Event::MessageCallRejected`) is emitted.
type CallFilter: Filter<<Self as Config<I>>::Call>;
/// The type that is used to wrap the `Self::Call` when it is moved over bridge. /// The type that is used to wrap the `Self::Call` when it is moved over bridge.
/// ///
/// The idea behind this is to avoid `Call` conversion/decoding until we'll be sure /// The idea behind this is to avoid `Call` conversion/decoding until we'll be sure
@@ -172,6 +177,8 @@ decl_event!(
MessageDispatched(InstanceId, MessageId, DispatchResult), MessageDispatched(InstanceId, MessageId, DispatchResult),
/// We have failed to decode Call from the message. /// We have failed to decode Call from the message.
MessageCallDecodeFailed(InstanceId, MessageId), MessageCallDecodeFailed(InstanceId, MessageId),
/// The call from the message has been rejected by the call filter.
MessageCallRejected(InstanceId, MessageId),
/// Phantom member, never used. Needed to handle multiple pallet instances. /// Phantom member, never used. Needed to handle multiple pallet instances.
_Dummy(PhantomData<I>), _Dummy(PhantomData<I>),
} }
@@ -269,6 +276,18 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
} }
}; };
// filter the call
if !T::CallFilter::filter(&call) {
frame_support::debug::trace!(
"Message {:?}/{:?}: the call ({:?}) is rejected by filter",
bridge,
id,
call,
);
Self::deposit_event(RawEvent::MessageCallRejected(bridge, id));
return;
}
// verify weight // verify weight
// (we want passed weight to be at least equal to pre-dispatch weight of the call // (we want passed weight to be at least equal to pre-dispatch weight of the call
// because otherwise Calls may be dispatched at lower price) // because otherwise Calls may be dispatched at lower price)
@@ -488,6 +507,7 @@ mod tests {
type TargetChainAccountPublic = TestAccountPublic; type TargetChainAccountPublic = TestAccountPublic;
type TargetChainSignature = TestSignature; type TargetChainSignature = TestSignature;
type Call = Call; type Call = Call;
type CallFilter = TestCallFilter;
type EncodedCall = EncodedCall; type EncodedCall = EncodedCall;
type AccountIdConverter = AccountIdConverter; type AccountIdConverter = AccountIdConverter;
} }
@@ -501,6 +521,14 @@ mod tests {
} }
} }
pub struct TestCallFilter;
impl Filter<Call> for TestCallFilter {
fn filter(call: &Call) -> bool {
!matches!(*call, Call::System(frame_system::Call::fill_block(_)))
}
}
const TEST_SPEC_VERSION: SpecVersion = 0; const TEST_SPEC_VERSION: SpecVersion = 0;
const TEST_WEIGHT: Weight = 1_000_000_000; const TEST_WEIGHT: Weight = 1_000_000_000;
@@ -668,6 +696,31 @@ mod tests {
}); });
} }
#[test]
fn should_emit_event_for_rejected_calls() {
new_test_ext().execute_with(|| {
let bridge = b"ethb".to_owned();
let id = [0; 4];
let call = Call::System(<frame_system::Call<TestRuntime>>::fill_block(Perbill::from_percent(75)));
let weight = call.get_dispatch_info().weight;
let mut message = prepare_root_message(call);
message.weight = weight;
System::set_block_number(1);
CallDispatch::dispatch(bridge, id, Ok(message));
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: TestEvent::call_dispatch(Event::<TestRuntime>::MessageCallRejected(bridge, id)),
topics: vec![],
}],
);
});
}
#[test] #[test]
fn should_dispatch_bridge_message_from_root_origin() { fn should_dispatch_bridge_message_from_root_origin() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {