Skip to content
This repository has been archived by the owner on Feb 29, 2024. It is now read-only.

Commit

Permalink
Enhance dispatch module (#121)
Browse files Browse the repository at this point in the history
  • Loading branch information
boundless-forest authored and jiguantong committed Jul 1, 2022
1 parent 5bf70ac commit f12048a
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 23 deletions.
64 changes: 43 additions & 21 deletions modules/dispatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
// Generated by `decl_event!`
#![allow(clippy::unused_unit)]

use bp_message_dispatch::{CallOrigin, MessageDispatch, MessagePayload, SpecVersion};
use bp_message_dispatch::{
CallFilter, CallOrigin, IntoDispatchOrigin, MessageDispatch, MessagePayload, SpecVersion,
};
use bp_runtime::{
derive_account_id,
messages::{DispatchFeePayment, MessageDispatchResult},
Expand All @@ -35,7 +37,7 @@ use codec::Encode;
use frame_support::{
dispatch::Dispatchable,
ensure,
traits::{Contains, Get},
traits::Get,
weights::{extract_actual_weight, GetDispatchInfo},
};
use frame_system::RawOrigin;
Expand Down Expand Up @@ -81,7 +83,7 @@ pub mod pallet {
///
/// 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: Contains<<Self as Config<I>>::Call>;
type CallFilter: CallFilter<Self::Origin, <Self as Config<I>>::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
Expand All @@ -93,6 +95,12 @@ pub mod pallet {
///
/// Used when deriving target chain AccountIds from source chain AccountIds.
type AccountIdConverter: sp_runtime::traits::Convert<sp_core::hash::H256, Self::AccountId>;
/// The type is used to customize the dispatch call origin.
type IntoDispatchOrigin: IntoDispatchOrigin<
Self::AccountId,
<Self as Config<I>>::Call,
Self::Origin,
>;
}

type BridgeMessageIdOf<T, I> = <T as Config<I>>::BridgeMessageId;
Expand Down Expand Up @@ -138,7 +146,9 @@ pub mod pallet {
}
}

impl<T: Config<I>, I: 'static> MessageDispatch<T::AccountId, T::BridgeMessageId> for Pallet<T, I> {
impl<T: Config<I>, I: 'static>
MessageDispatch<T::Origin, T::BridgeMessageId, <T as pallet::Config<I>>::Call> for Pallet<T, I>
{
type Message = MessagePayload<
T::SourceChainAccountId,
T::TargetChainAccountPublic,
Expand All @@ -150,7 +160,7 @@ impl<T: Config<I>, I: 'static> MessageDispatch<T::AccountId, T::BridgeMessageId>
message.weight
}

fn dispatch<P: FnOnce(&T::AccountId, bp_message_dispatch::Weight) -> Result<(), ()>>(
fn dispatch<P: FnOnce(&T::Origin, &<T as pallet::Config<I>>::Call) -> Result<(), ()>>(
source_chain: ChainId,
target_chain: ChainId,
id: T::BridgeMessageId,
Expand Down Expand Up @@ -217,7 +227,7 @@ impl<T: Config<I>, I: 'static> MessageDispatch<T::AccountId, T::BridgeMessageId>
};

// prepare dispatch origin
let origin_account = match message.origin {
let origin_derived_account = match message.origin {
CallOrigin::SourceRoot => {
let hex_id =
derive_account_id::<T::SourceChainAccountId>(source_chain, SourceAccount::Root);
Expand Down Expand Up @@ -260,8 +270,12 @@ impl<T: Config<I>, I: 'static> MessageDispatch<T::AccountId, T::BridgeMessageId>
},
};

// generate dispatch origin from origin account
let dispatch_origin =
T::IntoDispatchOrigin::into_dispatch_origin(&origin_derived_account, &call);

// filter the call
if !T::CallFilter::contains(&call) {
if !T::CallFilter::contains(&dispatch_origin, &call) {
log::trace!(
target: "runtime::bridge-dispatch",
"Message {:?}/{:?}: the call ({:?}) is rejected by filter",
Expand Down Expand Up @@ -299,9 +313,7 @@ impl<T: Config<I>, I: 'static> MessageDispatch<T::AccountId, T::BridgeMessageId>
// pay dispatch fee right before dispatch
let pay_dispatch_fee_at_target_chain =
message.dispatch_fee_payment == DispatchFeePayment::AtTargetChain;
if pay_dispatch_fee_at_target_chain
&& pay_dispatch_fee(&origin_account, message.weight).is_err()
{
if pay_dispatch_fee_at_target_chain && pay_dispatch_fee(&dispatch_origin, &call).is_err() {
log::trace!(
target: "runtime::bridge-dispatch",
"Failed to pay dispatch fee for dispatching message {:?}/{:?} with weight {}",
Expand All @@ -312,18 +324,15 @@ impl<T: Config<I>, I: 'static> MessageDispatch<T::AccountId, T::BridgeMessageId>
Self::deposit_event(Event::MessageDispatchPaymentFailed(
source_chain,
id,
origin_account,
origin_derived_account,
message.weight,
));
return dispatch_result;
}
dispatch_result.dispatch_fee_paid_during_dispatch = pay_dispatch_fee_at_target_chain;

// finally dispatch message
let origin = RawOrigin::Signed(origin_account).into();

log::trace!(target: "runtime::bridge-dispatch", "Message being dispatched is: {:.4096?}", &call);
let result = call.dispatch(origin);
let result = call.dispatch(dispatch_origin);
let actual_call_weight = extract_actual_weight(&result, &dispatch_info);
dispatch_result.dispatch_result = result.is_ok();
dispatch_result.unspent_weight = message.weight.saturating_sub(actual_call_weight);
Expand Down Expand Up @@ -529,6 +538,7 @@ mod tests {
type CallFilter = TestCallFilter;
type EncodedCall = EncodedCall;
type Event = Event;
type IntoDispatchOrigin = TestIntoDispatchOrigin;
type SourceChainAccountId = AccountId;
type TargetChainAccountPublic = TestAccountPublic;
type TargetChainSignature = TestSignature;
Expand All @@ -545,12 +555,20 @@ mod tests {

pub struct TestCallFilter;

impl Contains<Call> for TestCallFilter {
fn contains(call: &Call) -> bool {
impl CallFilter<Origin, Call> for TestCallFilter {
fn contains(_origin: &Origin, call: &Call) -> bool {
!matches!(*call, Call::System(frame_system::Call::fill_block { .. }))
}
}

pub struct TestIntoDispatchOrigin;

impl IntoDispatchOrigin<AccountId, Call, Origin> for TestIntoDispatchOrigin {
fn into_dispatch_origin(id: &AccountId, _call: &Call) -> Origin {
frame_system::RawOrigin::Signed(*id).into()
}
}

const TEST_SPEC_VERSION: SpecVersion = 0;
const TEST_WEIGHT: Weight = 1_000_000_000;

Expand All @@ -563,8 +581,9 @@ mod tests {
origin: CallOrigin<AccountId, TestAccountPublic, TestSignature>,
call: Call,
) -> <Pallet<TestRuntime> as MessageDispatch<
AccountId,
<TestRuntime as frame_system::Config>::Origin,
<TestRuntime as Config>::BridgeMessageId,
<TestRuntime as Config>::Call,
>>::Message {
MessagePayload {
spec_version: TEST_SPEC_VERSION,
Expand All @@ -578,17 +597,19 @@ mod tests {
fn prepare_root_message(
call: Call,
) -> <Pallet<TestRuntime> as MessageDispatch<
AccountId,
<TestRuntime as frame_system::Config>::Origin,
<TestRuntime as Config>::BridgeMessageId,
<TestRuntime as Config>::Call,
>>::Message {
prepare_message(CallOrigin::SourceRoot, call)
}

fn prepare_target_message(
call: Call,
) -> <Pallet<TestRuntime> as MessageDispatch<
AccountId,
<TestRuntime as frame_system::Config>::Origin,
<TestRuntime as Config>::BridgeMessageId,
<TestRuntime as Config>::Call,
>>::Message {
let origin = CallOrigin::TargetAccount(1, TestAccountPublic(1), TestSignature(1));
prepare_message(origin, call)
Expand All @@ -597,8 +618,9 @@ mod tests {
fn prepare_source_message(
call: Call,
) -> <Pallet<TestRuntime> as MessageDispatch<
AccountId,
<TestRuntime as frame_system::Config>::Origin,
<TestRuntime as Config>::BridgeMessageId,
<TestRuntime as Config>::Call,
>>::Message {
let origin = CallOrigin::SourceAccount(1);
prepare_message(origin, call)
Expand Down
19 changes: 17 additions & 2 deletions primitives/message-dispatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub type Weight = u64;
pub type SpecVersion = u32;

/// A generic trait to dispatch arbitrary messages delivered over the bridge.
pub trait MessageDispatch<AccountId, BridgeMessageId> {
pub trait MessageDispatch<Origin, BridgeMessageId, Call> {
/// A type of the message to be dispatched.
type Message: codec::Decode;

Expand All @@ -58,7 +58,7 @@ pub trait MessageDispatch<AccountId, BridgeMessageId> {
/// the whole message).
///
/// Returns unspent dispatch weight.
fn dispatch<P: FnOnce(&AccountId, Weight) -> Result<(), ()>>(
fn dispatch<P: FnOnce(&Origin, &Call) -> Result<(), ()>>(
source_chain: ChainId,
target_chain: ChainId,
id: BridgeMessageId,
Expand Down Expand Up @@ -140,3 +140,18 @@ impl<SourceChainAccountId, TargetChainAccountPublic, TargetChainSignature> Size
self.call.len() as _
}
}

/// Customize the dispatch origin before call dispatch.
pub trait IntoDispatchOrigin<AccountId, Call, Origin> {
/// Generate the dispatch origin for the given call.
///
/// Normally, the dispatch origin is one kind of frame_system::RawOrigin, however, sometimes
/// it is useful for a dispatch call with a custom origin.
fn into_dispatch_origin(id: &AccountId, call: &Call) -> Origin;
}

/// A generic trait to filter calls that are allowed to be dispatched.
pub trait CallFilter<Origin, Call> {
/// Filter the call, you might need origin to in the filter. return false, if not allowed.
fn contains(origin: &Origin, call: &Call) -> bool;
}

0 comments on commit f12048a

Please sign in to comment.