use super::*;
use frame_benchmarking::v2::*;
use frame_support::{assert_ok, weights::Weight};
use frame_system::RawOrigin;
use xcm::latest::prelude::*;
use xcm_builder::EnsureDelivery;
use xcm_executor::traits::FeeReason;
type RuntimeOrigin<T> = <T as frame_system::Config>::RuntimeOrigin;
pub struct Pallet<T: Config>(crate::Pallet<T>);
pub trait Config: crate::Config {
type DeliveryHelper: EnsureDelivery;
fn reachable_dest() -> Option<Location> {
None
}
fn teleportable_asset_and_dest() -> Option<(Asset, Location)> {
None
}
fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> {
None
}
fn set_up_complex_asset_transfer() -> Option<(Assets, u32, Location, Box<dyn FnOnce()>)> {
None
}
fn get_asset() -> Asset;
}
#[benchmarks]
mod benchmarks {
use super::*;
#[benchmark]
fn send() -> Result<(), BenchmarkError> {
let send_origin =
T::SendXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
if T::SendXcmOrigin::try_origin(send_origin.clone()).is_err() {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let msg = Xcm(vec![ClearOrigin]);
let versioned_dest: VersionedLocation = T::reachable_dest()
.ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?
.into();
let versioned_msg = VersionedXcm::from(msg);
T::DeliveryHelper::ensure_successful_delivery(
&Default::default(),
&versioned_dest.clone().try_into().unwrap(),
FeeReason::ChargeFees,
);
#[extrinsic_call]
_(send_origin as RuntimeOrigin<T>, Box::new(versioned_dest), Box::new(versioned_msg));
Ok(())
}
#[benchmark]
fn teleport_assets() -> Result<(), BenchmarkError> {
let (asset, destination) = T::teleportable_asset_and_dest()
.ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
let assets: Assets = asset.clone().into();
let caller: T::AccountId = whitelisted_caller();
let send_origin = RawOrigin::Signed(caller.clone());
let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
if !T::XcmTeleportFilter::contains(&(origin_location.clone(), assets.clone().into_inner()))
{
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let (_, _) = T::DeliveryHelper::ensure_successful_delivery(
&origin_location,
&destination,
FeeReason::ChargeFees,
);
match &asset.fun {
Fungible(amount) => {
<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::deposit_asset(
&Asset { fun: Fungible(*amount), id: asset.id },
&origin_location,
None,
)
.map_err(|error| {
tracing::error!("Fungible asset couldn't be deposited, error: {:?}", error);
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
})?;
},
NonFungible(_instance) => {
<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::deposit_asset(
&asset,
&origin_location,
None,
)
.map_err(|error| {
tracing::error!("Nonfungible asset couldn't be deposited, error: {:?}", error);
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
})?;
},
};
let recipient = [0u8; 32];
let versioned_dest: VersionedLocation = destination.into();
let versioned_beneficiary: VersionedLocation =
AccountId32 { network: None, id: recipient.into() }.into();
let versioned_assets: VersionedAssets = assets.into();
#[extrinsic_call]
_(
send_origin,
Box::new(versioned_dest),
Box::new(versioned_beneficiary),
Box::new(versioned_assets),
0,
);
Ok(())
}
#[benchmark]
fn reserve_transfer_assets() -> Result<(), BenchmarkError> {
let (asset, destination) = T::reserve_transferable_asset_and_dest()
.ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
let assets: Assets = asset.clone().into();
let caller: T::AccountId = whitelisted_caller();
let send_origin = RawOrigin::Signed(caller.clone());
let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
if !T::XcmReserveTransferFilter::contains(&(
origin_location.clone(),
assets.clone().into_inner(),
)) {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let (_, _) = T::DeliveryHelper::ensure_successful_delivery(
&origin_location,
&destination,
FeeReason::ChargeFees,
);
match &asset.fun {
Fungible(amount) => {
<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::deposit_asset(
&Asset { fun: Fungible(*amount), id: asset.id.clone() },
&origin_location,
None,
)
.map_err(|error| {
tracing::error!("Fungible asset couldn't be deposited, error: {:?}", error);
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
})?;
},
NonFungible(_instance) => {
<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::deposit_asset(
&asset,
&origin_location,
None,
)
.map_err(|error| {
tracing::error!("Nonfungible asset couldn't be deposited, error: {:?}", error);
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
})?;
},
};
let recipient = [0u8; 32];
let versioned_dest: VersionedLocation = destination.clone().into();
let versioned_beneficiary: VersionedLocation =
AccountId32 { network: None, id: recipient.into() }.into();
let versioned_assets: VersionedAssets = assets.into();
#[extrinsic_call]
_(
send_origin,
Box::new(versioned_dest),
Box::new(versioned_beneficiary),
Box::new(versioned_assets),
0,
);
match &asset.fun {
Fungible(amount) => {
assert_ok!(<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::withdraw_asset(
&Asset { fun: Fungible(*amount), id: asset.id },
&destination,
None,
));
},
NonFungible(_instance) => {
assert_ok!(<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::withdraw_asset(
&asset,
&destination,
None,
));
},
};
Ok(())
}
#[benchmark]
fn transfer_assets() -> Result<(), BenchmarkError> {
let (assets, _fee_index, destination, verify_fn) = T::set_up_complex_asset_transfer()
.ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
let caller: T::AccountId = whitelisted_caller();
let send_origin = RawOrigin::Signed(caller.clone());
let recipient = [0u8; 32];
let versioned_dest: VersionedLocation = destination.into();
let versioned_beneficiary: VersionedLocation =
AccountId32 { network: None, id: recipient.into() }.into();
let versioned_assets: VersionedAssets = assets.into();
T::DeliveryHelper::ensure_successful_delivery(
&Default::default(),
&versioned_dest.clone().try_into().unwrap(),
FeeReason::ChargeFees,
);
#[extrinsic_call]
_(
send_origin,
Box::new(versioned_dest),
Box::new(versioned_beneficiary),
Box::new(versioned_assets),
0,
WeightLimit::Unlimited,
);
verify_fn();
Ok(())
}
#[benchmark]
fn execute() -> Result<(), BenchmarkError> {
let execute_origin =
T::ExecuteXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let origin_location = T::ExecuteXcmOrigin::try_origin(execute_origin.clone())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
let msg = Xcm(vec![ClearOrigin]);
if !T::XcmExecuteFilter::contains(&(origin_location, msg.clone())) {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let versioned_msg = VersionedXcm::from(msg);
#[extrinsic_call]
_(execute_origin as RuntimeOrigin<T>, Box::new(versioned_msg), Weight::MAX);
Ok(())
}
#[benchmark]
fn force_xcm_version() -> Result<(), BenchmarkError> {
let loc = T::reachable_dest()
.ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
let xcm_version = 2;
#[extrinsic_call]
_(RawOrigin::Root, Box::new(loc), xcm_version);
Ok(())
}
#[benchmark]
fn force_default_xcm_version() {
#[extrinsic_call]
_(RawOrigin::Root, Some(2))
}
#[benchmark]
fn force_subscribe_version_notify() -> Result<(), BenchmarkError> {
let versioned_loc: VersionedLocation = T::reachable_dest()
.ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?
.into();
T::DeliveryHelper::ensure_successful_delivery(
&Default::default(),
&versioned_loc.clone().try_into().unwrap(),
FeeReason::ChargeFees,
);
#[extrinsic_call]
_(RawOrigin::Root, Box::new(versioned_loc));
Ok(())
}
#[benchmark]
fn force_unsubscribe_version_notify() -> Result<(), BenchmarkError> {
let loc = T::reachable_dest()
.ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
let versioned_loc: VersionedLocation = loc.clone().into();
T::DeliveryHelper::ensure_successful_delivery(
&Default::default(),
&versioned_loc.clone().try_into().unwrap(),
FeeReason::ChargeFees,
);
let _ = crate::Pallet::<T>::request_version_notify(loc);
#[extrinsic_call]
_(RawOrigin::Root, Box::new(versioned_loc));
Ok(())
}
#[benchmark]
fn force_suspension() {
#[extrinsic_call]
_(RawOrigin::Root, true)
}
#[benchmark]
fn migrate_supported_version() {
let old_version = XCM_VERSION - 1;
let loc = VersionedLocation::from(Location::from(Parent));
SupportedVersion::<T>::insert(old_version, loc, old_version);
#[block]
{
crate::Pallet::<T>::check_xcm_version_change(
VersionMigrationStage::MigrateSupportedVersion,
Weight::zero(),
);
}
}
#[benchmark]
fn migrate_version_notifiers() {
let old_version = XCM_VERSION - 1;
let loc = VersionedLocation::from(Location::from(Parent));
VersionNotifiers::<T>::insert(old_version, loc, 0);
#[block]
{
crate::Pallet::<T>::check_xcm_version_change(
VersionMigrationStage::MigrateVersionNotifiers,
Weight::zero(),
);
}
}
#[benchmark]
fn already_notified_target() -> Result<(), BenchmarkError> {
let loc = T::reachable_dest().ok_or(BenchmarkError::Override(
BenchmarkResult::from_weight(T::DbWeight::get().reads(1)),
))?;
let loc = VersionedLocation::from(loc);
let current_version = T::AdvertisedXcmVersion::get();
VersionNotifyTargets::<T>::insert(
current_version,
loc,
(0, Weight::zero(), current_version),
);
#[block]
{
crate::Pallet::<T>::check_xcm_version_change(
VersionMigrationStage::NotifyCurrentTargets(None),
Weight::zero(),
);
}
Ok(())
}
#[benchmark]
fn notify_current_targets() -> Result<(), BenchmarkError> {
let loc = T::reachable_dest().ok_or(BenchmarkError::Override(
BenchmarkResult::from_weight(T::DbWeight::get().reads_writes(1, 3)),
))?;
let loc = VersionedLocation::from(loc);
let current_version = T::AdvertisedXcmVersion::get();
let old_version = current_version - 1;
VersionNotifyTargets::<T>::insert(current_version, loc, (0, Weight::zero(), old_version));
#[block]
{
crate::Pallet::<T>::check_xcm_version_change(
VersionMigrationStage::NotifyCurrentTargets(None),
Weight::zero(),
);
}
Ok(())
}
#[benchmark]
fn notify_target_migration_fail() {
let newer_xcm_version = xcm::prelude::XCM_VERSION;
let older_xcm_version = newer_xcm_version - 1;
let bad_location: Location = Plurality { id: BodyId::Unit, part: BodyPart::Voice }.into();
let bad_location = VersionedLocation::from(bad_location)
.into_version(older_xcm_version)
.expect("Version convertion should work");
let current_version = T::AdvertisedXcmVersion::get();
VersionNotifyTargets::<T>::insert(
current_version,
bad_location,
(0, Weight::zero(), current_version),
);
#[block]
{
crate::Pallet::<T>::check_xcm_version_change(
VersionMigrationStage::MigrateAndNotifyOldTargets,
Weight::zero(),
);
}
}
#[benchmark]
fn migrate_version_notify_targets() {
let current_version = T::AdvertisedXcmVersion::get();
let old_version = current_version - 1;
let loc = VersionedLocation::from(Location::from(Parent));
VersionNotifyTargets::<T>::insert(old_version, loc, (0, Weight::zero(), current_version));
#[block]
{
crate::Pallet::<T>::check_xcm_version_change(
VersionMigrationStage::MigrateAndNotifyOldTargets,
Weight::zero(),
);
}
}
#[benchmark]
fn migrate_and_notify_old_targets() -> Result<(), BenchmarkError> {
let loc = T::reachable_dest().ok_or(BenchmarkError::Override(
BenchmarkResult::from_weight(T::DbWeight::get().reads_writes(1, 3)),
))?;
let loc = VersionedLocation::from(loc);
let old_version = T::AdvertisedXcmVersion::get() - 1;
VersionNotifyTargets::<T>::insert(old_version, loc, (0, Weight::zero(), old_version));
#[block]
{
crate::Pallet::<T>::check_xcm_version_change(
VersionMigrationStage::MigrateAndNotifyOldTargets,
Weight::zero(),
);
}
Ok(())
}
#[benchmark]
fn new_query() {
let responder = Location::from(Parent);
let timeout = 1u32.into();
let match_querier = Location::from(Here);
#[block]
{
crate::Pallet::<T>::new_query(responder, timeout, match_querier);
}
}
#[benchmark]
fn take_response() {
let responder = Location::from(Parent);
let timeout = 1u32.into();
let match_querier = Location::from(Here);
let query_id = crate::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<_>>();
crate::Pallet::<T>::expect_response(
query_id,
Response::PalletsInfo(infos.try_into().unwrap()),
);
#[block]
{
<crate::Pallet<T> as QueryHandler>::take_response(query_id);
}
}
#[benchmark]
fn claim_assets() -> Result<(), BenchmarkError> {
let claim_origin = RawOrigin::Signed(whitelisted_caller());
let claim_location = T::ExecuteXcmOrigin::try_origin(claim_origin.clone().into())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
let asset: Asset = T::get_asset();
crate::Pallet::<T>::drop_assets(
&claim_location,
asset.clone().into(),
&XcmContext { origin: None, message_id: [0u8; 32], topic: None },
);
let versioned_assets = VersionedAssets::from(Assets::from(asset));
#[extrinsic_call]
_(
claim_origin,
Box::new(versioned_assets),
Box::new(VersionedLocation::from(claim_location)),
);
Ok(())
}
impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext_with_balances(Vec::new()),
crate::mock::Test
);
}
pub mod helpers {
use super::*;
pub fn native_teleport_as_asset_transfer<T>(
native_asset_location: Location,
destination: Location,
) -> Option<(Assets, u32, Location, Box<dyn FnOnce()>)>
where
T: Config + pallet_balances::Config,
u128: From<<T as pallet_balances::Config>::Balance>,
{
let amount = T::ExistentialDeposit::get() * 100u32.into();
let assets: Assets =
Asset { fun: Fungible(amount.into()), id: AssetId(native_asset_location) }.into();
let fee_index = 0u32;
let balance = amount * 10u32.into();
let who = whitelisted_caller();
let _ =
<pallet_balances::Pallet::<T> as frame_support::traits::Currency<_>>::make_free_balance_be(&who, balance);
assert_eq!(pallet_balances::Pallet::<T>::free_balance(&who), balance);
let verify = Box::new(move || {
assert!(pallet_balances::Pallet::<T>::free_balance(&who) <= balance - amount);
});
Some((assets, fee_index, destination, verify))
}
}