From 8ee90afae6b279600e35f1b45745ec1b8e482cfb Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 1 Feb 2021 23:53:33 +0300 Subject: [PATCH] Pre-dispatch call filter (#687) * pre-dispatch call filter * swap filter <-> weight * clippy * fmt --- bridges/bin/millau/runtime/src/lib.rs | 1 + bridges/bin/rialto/runtime/src/lib.rs | 1 + bridges/modules/call-dispatch/src/lib.rs | 55 +++++++++++++++++++++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index d85be22b24..3164c8a9d8 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -215,6 +215,7 @@ impl pallet_bridge_call_dispatch::Config for Runtime { type Event = Event; type MessageId = (bp_message_lane::LaneId, bp_message_lane::MessageNonce); type Call = Call; + type CallFilter = (); type EncodedCall = crate::rialto_messages::FromRialtoEncodedCall; type SourceChainAccountId = bp_rialto::AccountId; type TargetChainAccountPublic = MultiSigner; diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs index d45164f6ad..677ca283c4 100644 --- a/bridges/bin/rialto/runtime/src/lib.rs +++ b/bridges/bin/rialto/runtime/src/lib.rs @@ -266,6 +266,7 @@ impl pallet_bridge_call_dispatch::Config for Runtime { type Event = Event; type MessageId = (bp_message_lane::LaneId, bp_message_lane::MessageNonce); type Call = Call; + type CallFilter = (); type EncodedCall = crate::millau_messages::FromMillauEncodedCall; type SourceChainAccountId = bp_millau::AccountId; type TargetChainAccountPublic = MultiSigner; diff --git a/bridges/modules/call-dispatch/src/lib.rs b/bridges/modules/call-dispatch/src/lib.rs index b68ff3aa5e..1f932dcfe3 100644 --- a/bridges/modules/call-dispatch/src/lib.rs +++ b/bridges/modules/call-dispatch/src/lib.rs @@ -31,7 +31,7 @@ use frame_support::{ decl_event, decl_module, decl_storage, dispatch::{Dispatchable, Parameter}, ensure, - traits::Get, + traits::{Filter, Get}, weights::{extract_actual_weight, GetDispatchInfo}, RuntimeDebug, }; @@ -134,6 +134,11 @@ pub trait Config: frame_system::Config { Origin = ::Origin, 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<>::Call>; /// 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 @@ -172,6 +177,8 @@ decl_event!( MessageDispatched(InstanceId, MessageId, DispatchResult), /// We have failed to decode Call from the message. 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. _Dummy(PhantomData), } @@ -269,6 +276,18 @@ impl, I: Instance> MessageDispatch for Module { } }; + // 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 // (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) @@ -488,6 +507,7 @@ mod tests { type TargetChainAccountPublic = TestAccountPublic; type TargetChainSignature = TestSignature; type Call = Call; + type CallFilter = TestCallFilter; type EncodedCall = EncodedCall; type AccountIdConverter = AccountIdConverter; } @@ -501,6 +521,14 @@ mod tests { } } + pub struct TestCallFilter; + + impl Filter for TestCallFilter { + fn filter(call: &Call) -> bool { + !matches!(*call, Call::System(frame_system::Call::fill_block(_))) + } + } + const TEST_SPEC_VERSION: SpecVersion = 0; 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(>::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::::MessageCallRejected(bridge, id)), + topics: vec![], + }], + ); + }); + } + #[test] fn should_dispatch_bridge_message_from_root_origin() { new_test_ext().execute_with(|| {