lib.rs 116 KiB
Newer Older
Gavin Wood's avatar
Gavin Wood committed
						return Weight::zero()
					},
					_ => {
						Self::deposit_event(Event::InvalidResponder {
Francisco Aguirre's avatar
Francisco Aguirre committed
							origin: origin.clone(),
							query_id,
							expected_location: None,
						});
Gavin Wood's avatar
Gavin Wood committed
						// TODO #3735: Correct weight for this.
						return Weight::zero()
					},
				};
				// TODO #3735: Check max_weight is correct.
				if !is_active {
					Queries::<T>::insert(
						query_id,
Francisco Aguirre's avatar
Francisco Aguirre committed
						QueryStatus::VersionNotifier {
							origin: origin.clone().into(),
							is_active: true,
						},
Gavin Wood's avatar
Gavin Wood committed
				}
				// We're being notified of a version change.
Francisco Aguirre's avatar
Francisco Aguirre committed
				SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
				Self::deposit_event(Event::SupportedVersionChanged {
					location: origin,
					version: v,
				});
Gavin Wood's avatar
Gavin Wood committed
				Weight::zero()
			},
			(
				response,
				Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
			) => {
				if let Some(match_querier) = maybe_match_querier {
Francisco Aguirre's avatar
Francisco Aguirre committed
					let match_querier = match Location::try_from(match_querier) {
Gavin Wood's avatar
Gavin Wood committed
						Ok(mq) => mq,
Francisco Aguirre's avatar
Francisco Aguirre committed
							Self::deposit_event(Event::InvalidQuerierVersion {
								origin: origin.clone(),
								query_id,
							});
Gavin Wood's avatar
Gavin Wood committed
							return Weight::zero()
Gavin Wood's avatar
Gavin Wood committed
					if querier.map_or(true, |q| q != &match_querier) {
						Self::deposit_event(Event::InvalidQuerier {
Francisco Aguirre's avatar
Francisco Aguirre committed
							origin: origin.clone(),
							expected_querier: match_querier,
							maybe_actual_querier: querier.cloned(),
						});
Gavin Wood's avatar
Gavin Wood committed
						return Weight::zero()
Gavin Wood's avatar
Gavin Wood committed
				}
Francisco Aguirre's avatar
Francisco Aguirre committed
				let responder = match Location::try_from(responder) {
Gavin Wood's avatar
Gavin Wood committed
					Ok(r) => r,
					Err(_) => {
Francisco Aguirre's avatar
Francisco Aguirre committed
						Self::deposit_event(Event::InvalidResponderVersion {
							origin: origin.clone(),
							query_id,
						});
Gavin Wood's avatar
Gavin Wood committed
						return Weight::zero()
					},
				};
				if origin != responder {
					Self::deposit_event(Event::InvalidResponder {
Francisco Aguirre's avatar
Francisco Aguirre committed
						origin: origin.clone(),
Gavin Wood's avatar
Gavin Wood committed
						query_id,
						expected_location: Some(responder),
					});
Gavin Wood's avatar
Gavin Wood committed
					return Weight::zero()
				}
				return match maybe_notify {
					Some((pallet_index, call_index)) => {
						// This is a bit horrible, but we happen to know that the `Call` will
						// be built by `(pallet_index: u8, call_index: u8, QueryId, Response)`.
						// So we just encode that and then re-encode to a real Call.
						let bare = (pallet_index, call_index, query_id, response);
						if let Ok(call) = bare.using_encoded(|mut bytes| {
							<T as Config>::RuntimeCall::decode(&mut bytes)
						}) {
							Queries::<T>::remove(query_id);
							let weight = call.get_dispatch_info().weight;
							if weight.any_gt(max_weight) {
								let e = Event::NotifyOverweight {
Gavin Wood's avatar
Gavin Wood committed
									query_id,
									pallet_index,
									call_index,
									actual_weight: weight,
									max_budgeted_weight: max_weight,
								};
Gavin Wood's avatar
Gavin Wood committed
								Self::deposit_event(e);
								return Weight::zero()
							}
Francisco Aguirre's avatar
Francisco Aguirre committed
							let dispatch_origin = Origin::Response(origin.clone()).into();
Gavin Wood's avatar
Gavin Wood committed
							match call.dispatch(dispatch_origin) {
								Ok(post_info) => {
									let e = Event::Notified { query_id, pallet_index, call_index };
Gavin Wood's avatar
Gavin Wood committed
									Self::deposit_event(e);
									post_info.actual_weight
								},
								Err(error_and_info) => {
									let e = Event::NotifyDispatchError {
									// Not much to do with the result as it is. It's up to the
									// parachain to ensure that the message makes sense.
Gavin Wood's avatar
Gavin Wood committed
									error_and_info.post_info.actual_weight
								},
Gavin Wood's avatar
Gavin Wood committed
							.unwrap_or(weight)
						} else {
							let e =
								Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
							Self::deposit_event(e);
Gavin Wood's avatar
Gavin Wood committed
							Weight::zero()
						}
					},
					None => {
						let e = Event::ResponseReady { query_id, response: response.clone() };
Gavin Wood's avatar
Gavin Wood committed
						Self::deposit_event(e);
						let at = frame_system::Pallet::<T>::current_block_number();
						let response = response.into();
						Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
						Weight::zero()
					},
				}
			},
			_ => {
Francisco Aguirre's avatar
Francisco Aguirre committed
				let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
				Self::deposit_event(e);
Gavin Wood's avatar
Gavin Wood committed
				Weight::zero()
			},
impl<T: Config> CheckSuspension for Pallet<T> {
	fn is_suspended<Call>(
Francisco Aguirre's avatar
Francisco Aguirre committed
		_origin: &Location,
		_instructions: &mut [Instruction<Call>],
		_max_weight: Weight,
		_properties: &mut Properties,
	) -> bool {
		XcmExecutionSuspended::<T>::get()
	}
}

/// Ensure that the origin `o` represents an XCM (`Transact`) origin.
///
/// Returns `Ok` with the location of the XCM sender or an `Err` otherwise.
Francisco Aguirre's avatar
Francisco Aguirre committed
pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
where
	OuterOrigin: Into<Result<Origin, OuterOrigin>>,
Gavin Wood's avatar
Gavin Wood committed
{
	match o.into() {
		Ok(Origin::Xcm(location)) => Ok(location),
		_ => Err(BadOrigin),
	}
}

/// Ensure that the origin `o` represents an XCM response origin.
///
/// Returns `Ok` with the location of the responder or an `Err` otherwise.
Francisco Aguirre's avatar
Francisco Aguirre committed
pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
where
	OuterOrigin: Into<Result<Origin, OuterOrigin>>,
{
	match o.into() {
		Ok(Origin::Response(location)) => Ok(location),
		_ => Err(BadOrigin),
	}
}

Francisco Aguirre's avatar
Francisco Aguirre committed
/// Filter for `Location` to find those which represent a strict majority approval of an
Gavin Wood's avatar
Gavin Wood committed
///
/// May reasonably be used with `EnsureXcm`.
pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
Francisco Aguirre's avatar
Francisco Aguirre committed
impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
	for IsMajorityOfBody<Prefix, Body>
{
Francisco Aguirre's avatar
Francisco Aguirre committed
	fn contains(l: &Location) -> bool {
		let maybe_suffix = l.match_and_split(&Prefix::get());
		matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
Francisco Aguirre's avatar
Francisco Aguirre committed
/// Filter for `Location` to find those which represent a voice of an identified plurality.
///
/// May reasonably be used with `EnsureXcm`.
pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
Francisco Aguirre's avatar
Francisco Aguirre committed
impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
	fn contains(l: &Location) -> bool {
		let maybe_suffix = l.match_and_split(&Prefix::get());
		matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
	}
}

Francisco Aguirre's avatar
Francisco Aguirre committed
/// `EnsureOrigin` implementation succeeding with a `Location` value to recognize and filter
Francisco Aguirre's avatar
Francisco Aguirre committed
pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
impl<
		O: OriginTrait + From<Origin>,
		F: Contains<L>,
		L: TryFrom<Location> + TryInto<Location> + Clone,
	> EnsureOrigin<O> for EnsureXcm<F, L>
where
	O::PalletsOrigin: From<Origin> + TryInto<Origin, Error = O::PalletsOrigin>,
Francisco Aguirre's avatar
Francisco Aguirre committed
	type Success = L;
Gavin Wood's avatar
Gavin Wood committed

	fn try_origin(outer: O) -> Result<Self::Success, O> {
		outer.try_with_caller(|caller| {
			caller.try_into().and_then(|o| match o {
Francisco Aguirre's avatar
Francisco Aguirre committed
				Origin::Xcm(ref location)
					if F::contains(&location.clone().try_into().map_err(|_| o.clone().into())?) =>
					Ok(location.clone().try_into().map_err(|_| o.clone().into())?),
				Origin::Xcm(location) => Err(Origin::Xcm(location).into()),
				o => Err(o.into()),
Gavin Wood's avatar
Gavin Wood committed
	}

	#[cfg(feature = "runtime-benchmarks")]
	fn try_successful_origin() -> Result<O, ()> {
		Ok(O::from(Origin::Xcm(Here.into())))
Francisco Aguirre's avatar
Francisco Aguirre committed
/// `EnsureOrigin` implementation succeeding with a `Location` value to recognize and filter
/// the `Origin::Response` item.
pub struct EnsureResponse<F>(PhantomData<F>);
Francisco Aguirre's avatar
Francisco Aguirre committed
impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
where
	O::PalletsOrigin: From<Origin> + TryInto<Origin, Error = O::PalletsOrigin>,
{
Francisco Aguirre's avatar
Francisco Aguirre committed
	type Success = Location;

	fn try_origin(outer: O) -> Result<Self::Success, O> {
		outer.try_with_caller(|caller| {
			caller.try_into().and_then(|o| match o {
				Origin::Response(responder) => Ok(responder),
				o => Err(o.into()),
			})
		})
	}

	#[cfg(feature = "runtime-benchmarks")]
	fn try_successful_origin() -> Result<O, ()> {
		Ok(O::from(Origin::Response(Here.into())))
Francisco Aguirre's avatar
Francisco Aguirre committed
/// A simple passthrough where we reuse the `Location`-typed XCM origin as the inner value of
/// this crate's `Origin::Xcm` value.
Sergej Sakac's avatar
Sergej Sakac committed
pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
	for XcmPassthrough<RuntimeOrigin>
{
Francisco Aguirre's avatar
Francisco Aguirre committed
		origin: impl Into<Location>,
Francisco Aguirre's avatar
Francisco Aguirre committed
	) -> Result<RuntimeOrigin, Location> {
		match kind {
			OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
			_ => Err(origin),