mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 04:01:02 +00:00
Contracts: Add XCM traits to interface with contracts (#2086)
We are introducing a new set of `XcmController` traits (final name yet to be determined). These traits are implemented by `pallet-xcm` and allows other pallets, such as `pallet_contracts`, to rely on these traits instead of tight coupling them to `pallet-xcm`. Using only the existing Xcm traits would mean duplicating the logic from `pallet-xcm` in these other pallets, which we aim to avoid. Our objective is to ensure that when these APIs are called from `pallet-contracts`, they produce the exact same outcomes as if called directly from `pallet-xcm`. The other benefits is that we can also expose return values to `pallet-contracts` instead of just calling `pallet-xcm` dispatchable and getting a `DispatchResult` back. See traits integration in this PR https://github.com/paritytech/polkadot-sdk/pull/1248, where the traits are used as follow to define and implement `pallet-contracts` Config. ```rs // Contracts config: pub trait Config: frame_system::Config { // ... /// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and /// execute XCM programs. type Xcm: xcm_executor::traits::Controller< OriginFor<Self>, <Self as frame_system::Config>::RuntimeCall, BlockNumberFor<Self>, >; } // implementation impl pallet_contracts::Config for Runtime { // ... type Xcm = pallet_xcm::Pallet<Self>; } ``` --------- Co-authored-by: Alexander Theißen <alex.theissen@me.com> Co-authored-by: command-bot <>
This commit is contained in:
@@ -190,6 +190,33 @@ benchmarks! {
|
||||
Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateAndNotifyOldTargets, Weight::zero());
|
||||
}
|
||||
|
||||
new_query {
|
||||
let responder = MultiLocation::from(Parent);
|
||||
let timeout = 1u32.into();
|
||||
let match_querier = MultiLocation::from(Here);
|
||||
}: {
|
||||
Pallet::<T>::new_query(responder, timeout, match_querier);
|
||||
}
|
||||
|
||||
take_response {
|
||||
let responder = MultiLocation::from(Parent);
|
||||
let timeout = 1u32.into();
|
||||
let match_querier = MultiLocation::from(Here);
|
||||
let query_id = Pallet::<T>::new_query(responder, timeout, match_querier);
|
||||
let infos = (0 .. xcm::v3::MaxPalletsInfo::get()).map(|_| PalletInfo::new(
|
||||
u32::MAX,
|
||||
(0..xcm::v3::MaxPalletNameLen::get()).map(|_| 97u8).collect::<Vec<_>>().try_into().unwrap(),
|
||||
(0..xcm::v3::MaxPalletNameLen::get()).map(|_| 97u8).collect::<Vec<_>>().try_into().unwrap(),
|
||||
u32::MAX,
|
||||
u32::MAX,
|
||||
u32::MAX,
|
||||
).unwrap()).collect::<Vec<_>>();
|
||||
Pallet::<T>::expect_response(query_id, Response::PalletsInfo(infos.try_into().unwrap()));
|
||||
|
||||
}: {
|
||||
<Pallet::<T> as QueryHandler>::take_response(query_id);
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(
|
||||
Pallet,
|
||||
crate::mock::new_test_ext_with_balances(Vec::new()),
|
||||
|
||||
@@ -28,9 +28,17 @@ mod tests;
|
||||
pub mod migration;
|
||||
|
||||
use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
|
||||
use frame_support::traits::{
|
||||
Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Get, LockableCurrency, OriginTrait,
|
||||
use frame_support::{
|
||||
dispatch::GetDispatchInfo,
|
||||
pallet_prelude::*,
|
||||
traits::{
|
||||
Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Get, LockableCurrency,
|
||||
OriginTrait, WithdrawReasons,
|
||||
},
|
||||
PalletId,
|
||||
};
|
||||
use frame_system::pallet_prelude::{BlockNumberFor, *};
|
||||
pub use pallet::*;
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::{
|
||||
traits::{
|
||||
@@ -41,17 +49,15 @@ use sp_runtime::{
|
||||
};
|
||||
use sp_std::{boxed::Box, marker::PhantomData, prelude::*, result::Result, vec};
|
||||
use xcm::{latest::QueryResponseInfo, prelude::*};
|
||||
use xcm_executor::traits::{ConvertOrigin, Properties};
|
||||
|
||||
use frame_support::{
|
||||
dispatch::GetDispatchInfo, pallet_prelude::*, traits::WithdrawReasons, PalletId,
|
||||
use xcm_builder::{
|
||||
ExecuteController, ExecuteControllerWeightInfo, QueryController, QueryControllerWeightInfo,
|
||||
SendController, SendControllerWeightInfo,
|
||||
};
|
||||
use frame_system::pallet_prelude::*;
|
||||
pub use pallet::*;
|
||||
use xcm_executor::{
|
||||
traits::{
|
||||
CheckSuspension, ClaimAssets, ConvertLocation, DropAssets, MatchesFungible, OnResponse,
|
||||
QueryHandler, QueryResponseStatus, VersionChangeNotifier, WeightBounds,
|
||||
CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin, DropAssets, MatchesFungible,
|
||||
OnResponse, Properties, QueryHandler, QueryResponseStatus, VersionChangeNotifier,
|
||||
WeightBounds,
|
||||
},
|
||||
Assets,
|
||||
};
|
||||
@@ -73,6 +79,8 @@ pub trait WeightInfo {
|
||||
fn notify_target_migration_fail() -> Weight;
|
||||
fn migrate_version_notify_targets() -> Weight;
|
||||
fn migrate_and_notify_old_targets() -> Weight;
|
||||
fn new_query() -> Weight;
|
||||
fn take_response() -> Weight;
|
||||
}
|
||||
|
||||
/// fallback implementation
|
||||
@@ -141,6 +149,14 @@ impl WeightInfo for TestWeightInfo {
|
||||
fn migrate_and_notify_old_targets() -> Weight {
|
||||
Weight::from_parts(100_000_000, 0)
|
||||
}
|
||||
|
||||
fn new_query() -> Weight {
|
||||
Weight::from_parts(100_000_000, 0)
|
||||
}
|
||||
|
||||
fn take_response() -> Weight {
|
||||
Weight::from_parts(100_000_000, 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[frame_support::pallet]
|
||||
@@ -267,6 +283,93 @@ pub mod pallet {
|
||||
type ReachableDest: Get<Option<MultiLocation>>;
|
||||
}
|
||||
|
||||
impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
|
||||
fn execute() -> Weight {
|
||||
T::WeightInfo::execute()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
|
||||
type WeightInfo = Self;
|
||||
fn execute(
|
||||
origin: OriginFor<T>,
|
||||
message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
|
||||
max_weight: Weight,
|
||||
) -> Result<Outcome, DispatchError> {
|
||||
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
|
||||
let hash = message.using_encoded(sp_io::hashing::blake2_256);
|
||||
let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
|
||||
let value = (origin_location, message);
|
||||
ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
|
||||
let (origin_location, message) = value;
|
||||
let outcome = T::XcmExecutor::execute_xcm_in_credit(
|
||||
origin_location,
|
||||
message,
|
||||
hash,
|
||||
max_weight,
|
||||
max_weight,
|
||||
);
|
||||
Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
|
||||
Ok(outcome)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SendControllerWeightInfo for Pallet<T> {
|
||||
fn send() -> Weight {
|
||||
T::WeightInfo::send()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SendController<OriginFor<T>> for Pallet<T> {
|
||||
type WeightInfo = Self;
|
||||
fn send(
|
||||
origin: OriginFor<T>,
|
||||
dest: Box<VersionedMultiLocation>,
|
||||
message: Box<VersionedXcm<()>>,
|
||||
) -> Result<XcmHash, DispatchError> {
|
||||
let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
|
||||
let interior: Junctions =
|
||||
origin_location.try_into().map_err(|_| Error::<T>::InvalidOrigin)?;
|
||||
let dest = MultiLocation::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
|
||||
let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
|
||||
|
||||
let message_id =
|
||||
Self::send_xcm(interior, dest, message.clone()).map_err(Error::<T>::from)?;
|
||||
let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
|
||||
Self::deposit_event(e);
|
||||
Ok(message_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> QueryControllerWeightInfo for Pallet<T> {
|
||||
fn query() -> Weight {
|
||||
T::WeightInfo::new_query()
|
||||
}
|
||||
fn take_response() -> Weight {
|
||||
T::WeightInfo::take_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pallet<T> {
|
||||
type WeightInfo = Self;
|
||||
|
||||
fn query(
|
||||
origin: OriginFor<T>,
|
||||
timeout: BlockNumberFor<T>,
|
||||
match_querier: VersionedMultiLocation,
|
||||
) -> Result<Self::QueryId, DispatchError> {
|
||||
let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
|
||||
let query_id = <Self as QueryHandler>::new_query(
|
||||
responder,
|
||||
timeout,
|
||||
MultiLocation::try_from(match_querier)
|
||||
.map_err(|_| Into::<DispatchError>::into(Error::<T>::BadVersion))?,
|
||||
);
|
||||
|
||||
Ok(query_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
@@ -771,16 +874,7 @@ pub mod pallet {
|
||||
dest: Box<VersionedMultiLocation>,
|
||||
message: Box<VersionedXcm<()>>,
|
||||
) -> DispatchResult {
|
||||
let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
|
||||
let interior: Junctions =
|
||||
origin_location.try_into().map_err(|_| Error::<T>::InvalidOrigin)?;
|
||||
let dest = MultiLocation::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
|
||||
let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
|
||||
|
||||
let message_id =
|
||||
Self::send_xcm(interior, dest, message.clone()).map_err(Error::<T>::from)?;
|
||||
let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
|
||||
Self::deposit_event(e);
|
||||
<Self as SendController<_>>::send(origin, dest, message)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -896,7 +990,7 @@ pub mod pallet {
|
||||
/// execution attempt will be made.
|
||||
///
|
||||
/// NOTE: A successful return to this does *not* imply that the `msg` was executed
|
||||
/// successfully to completion; only that *some* of it was executed.
|
||||
/// successfully to completion; only that it was attempted.
|
||||
#[pallet::call_index(3)]
|
||||
#[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
|
||||
pub fn execute(
|
||||
@@ -904,23 +998,8 @@ pub mod pallet {
|
||||
message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
|
||||
max_weight: Weight,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
|
||||
let hash = message.using_encoded(sp_io::hashing::blake2_256);
|
||||
let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
|
||||
let value = (origin_location, message);
|
||||
ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
|
||||
let (origin_location, message) = value;
|
||||
let outcome = T::XcmExecutor::execute_xcm_in_credit(
|
||||
origin_location,
|
||||
message,
|
||||
hash,
|
||||
max_weight,
|
||||
max_weight,
|
||||
);
|
||||
let result =
|
||||
Ok(Some(outcome.weight_used().saturating_add(T::WeightInfo::execute())).into());
|
||||
Self::deposit_event(Event::Attempted { outcome });
|
||||
result
|
||||
let outcome = <Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
|
||||
Ok(Some(outcome.weight_used().saturating_add(T::WeightInfo::execute())).into())
|
||||
}
|
||||
|
||||
/// Extoll that a particular destination can be communicated with through a particular
|
||||
@@ -1145,7 +1224,7 @@ impl<T: Config> QueryHandler for Pallet<T> {
|
||||
timeout: BlockNumberFor<T>,
|
||||
match_querier: impl Into<MultiLocation>,
|
||||
) -> Self::QueryId {
|
||||
Self::do_new_query(responder, None, timeout, match_querier).into()
|
||||
Self::do_new_query(responder, None, timeout, match_querier)
|
||||
}
|
||||
|
||||
/// To check the status of the query, use `fn query()` passing the resultant `QueryId`
|
||||
|
||||
Reference in New Issue
Block a user