#![cfg(feature = "runtime-benchmarks")]
use crate::{
configuration::Pallet as Configuration,
hrmp::{Pallet as Hrmp, *},
paras::{Pallet as Paras, ParaKind, ParachainsCache},
shared::Pallet as Shared,
};
use frame_benchmarking::{v2::*, whitelisted_caller};
use frame_support::{assert_ok, traits::Currency};
type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
fn register_parachain_with_balance<T: Config>(id: ParaId, balance: BalanceOf<T>) {
let mut parachains = ParachainsCache::new();
Paras::<T>::initialize_para_now(
&mut parachains,
id,
&crate::paras::ParaGenesisArgs {
para_kind: ParaKind::Parachain,
genesis_head: vec![1].into(),
validation_code: vec![1].into(),
},
);
T::Currency::make_free_balance_be(&id.into_account_truncating(), balance);
}
fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
let events = frame_system::Pallet::<T>::events();
let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
assert_eq!(event, &system_event);
}
fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
let events = frame_system::Pallet::<T>::events();
let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
assert!(events.iter().any(|record| record.event == system_event));
}
enum ParachainSetupStep {
Requested,
Accepted,
Established,
CloseRequested,
}
fn establish_para_connection<T: Config>(
from: u32,
to: u32,
until: ParachainSetupStep,
) -> [(ParaId, crate::Origin); 2]
where
<T as frame_system::Config>::RuntimeOrigin: From<crate::Origin>,
{
let config = configuration::ActiveConfig::<T>::get();
let ed = T::Currency::minimum_balance();
let deposit: BalanceOf<T> = config.hrmp_sender_deposit.unique_saturated_into();
let capacity = config.hrmp_channel_max_capacity;
let message_size = config.hrmp_channel_max_message_size;
assert!(message_size > 0, "Invalid genesis for benchmarking");
assert!(capacity > 0, "Invalid genesis for benchmarking");
let sender: ParaId = from.into();
let sender_origin: crate::Origin = from.into();
let recipient: ParaId = to.into();
let recipient_origin: crate::Origin = to.into();
let output = [(sender, sender_origin.clone()), (recipient, recipient_origin.clone())];
if !Paras::<T>::is_parachain(sender) {
register_parachain_with_balance::<T>(sender, deposit + ed);
}
if !Paras::<T>::is_parachain(recipient) {
register_parachain_with_balance::<T>(recipient, deposit + ed);
}
assert_ok!(Hrmp::<T>::hrmp_init_open_channel(
sender_origin.clone().into(),
recipient,
capacity,
message_size
));
if matches!(until, ParachainSetupStep::Requested) {
return output
}
assert_ok!(Hrmp::<T>::hrmp_accept_open_channel(recipient_origin.into(), sender));
if matches!(until, ParachainSetupStep::Accepted) {
return output
}
Hrmp::<T>::process_hrmp_open_channel_requests(&configuration::ActiveConfig::<T>::get());
if matches!(until, ParachainSetupStep::Established) {
return output
}
let channel_id = HrmpChannelId { sender, recipient };
assert_ok!(Hrmp::<T>::hrmp_close_channel(sender_origin.clone().into(), channel_id));
if matches!(until, ParachainSetupStep::CloseRequested) {
return output
}
output
}
const PREFIX_0: u32 = 10_000;
const PREFIX_1: u32 = PREFIX_0 * 2;
const MAX_UNIQUE_CHANNELS: u32 = 128;
static_assertions::const_assert!(MAX_UNIQUE_CHANNELS < PREFIX_0);
static_assertions::const_assert!(HRMP_MAX_INBOUND_CHANNELS_BOUND < PREFIX_0);
static_assertions::const_assert!(HRMP_MAX_OUTBOUND_CHANNELS_BOUND < PREFIX_0);
#[benchmarks(where <T as frame_system::Config>::RuntimeOrigin: From<crate::Origin>)]
mod benchmarks {
use super::*;
#[benchmark]
fn hrmp_init_open_channel() {
let sender_id: ParaId = 1u32.into();
let sender_origin: crate::Origin = 1u32.into();
let recipient_id: ParaId = 2u32.into();
let ed = T::Currency::minimum_balance();
let deposit: BalanceOf<T> = configuration::ActiveConfig::<T>::get()
.hrmp_sender_deposit
.unique_saturated_into();
register_parachain_with_balance::<T>(sender_id, deposit + ed);
register_parachain_with_balance::<T>(recipient_id, deposit + ed);
let capacity = configuration::ActiveConfig::<T>::get().hrmp_channel_max_capacity;
let message_size = configuration::ActiveConfig::<T>::get().hrmp_channel_max_message_size;
#[extrinsic_call]
_(sender_origin, recipient_id, capacity, message_size);
assert_last_event::<T>(
Event::<T>::OpenChannelRequested {
sender: sender_id,
recipient: recipient_id,
proposed_max_capacity: capacity,
proposed_max_message_size: message_size,
}
.into(),
);
}
#[benchmark]
fn hrmp_accept_open_channel() {
let [(sender, _), (recipient, recipient_origin)] =
establish_para_connection::<T>(1, 2, ParachainSetupStep::Requested);
#[extrinsic_call]
_(recipient_origin, sender);
assert_last_event::<T>(Event::<T>::OpenChannelAccepted { sender, recipient }.into());
}
#[benchmark]
fn hrmp_close_channel() {
let [(sender, sender_origin), (recipient, _)] =
establish_para_connection::<T>(1, 2, ParachainSetupStep::Established);
let channel_id = HrmpChannelId { sender, recipient };
#[extrinsic_call]
_(sender_origin, channel_id.clone());
assert_last_event::<T>(
Event::<T>::ChannelClosed { by_parachain: sender, channel_id }.into(),
);
}
#[benchmark]
fn force_clean_hrmp(
i: Linear<0, { HRMP_MAX_INBOUND_CHANNELS_BOUND - 1 }>,
e: Linear<0, { HRMP_MAX_OUTBOUND_CHANNELS_BOUND - 1 }>,
) {
assert_ok!(Configuration::<T>::set_hrmp_max_parachain_outbound_channels(
frame_system::RawOrigin::Root.into(),
e + 1
));
assert_ok!(Configuration::<T>::set_hrmp_max_parachain_inbound_channels(
frame_system::RawOrigin::Root.into(),
i + 1
));
assert_ok!(Configuration::<T>::set_max_downward_message_size(
frame_system::RawOrigin::Root.into(),
1024
));
Configuration::<T>::initializer_on_new_session(&Shared::<T>::scheduled_session());
let config = configuration::ActiveConfig::<T>::get();
let deposit: BalanceOf<T> = config.hrmp_sender_deposit.unique_saturated_into();
let para: ParaId = 1u32.into();
register_parachain_with_balance::<T>(para, deposit);
T::Currency::make_free_balance_be(¶.into_account_truncating(), deposit * 256u32.into());
for ingress_para_id in 0..i {
let ingress_para_id = ingress_para_id + PREFIX_0;
let _ = establish_para_connection::<T>(
ingress_para_id,
para.into(),
ParachainSetupStep::Established,
);
}
assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default(), 0);
for egress_para_id in 0..e {
let egress_para_id = egress_para_id + PREFIX_1;
let _ = establish_para_connection::<T>(
para.into(),
egress_para_id,
ParachainSetupStep::Established,
);
}
assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default(), 0);
assert_eq!(HrmpChannels::<T>::iter().count() as u32, i + e);
#[extrinsic_call]
_(frame_system::Origin::<T>::Root, para, i, e);
assert_eq!(HrmpChannels::<T>::iter().count() as u32, 0);
Hrmp::<T>::assert_storage_consistency_exhaustive();
}
#[benchmark]
fn force_process_hrmp_open(
c: Linear<0, MAX_UNIQUE_CHANNELS>,
) {
for id in 0..c {
let _ = establish_para_connection::<T>(
PREFIX_0 + id,
PREFIX_1 + id,
ParachainSetupStep::Accepted,
);
}
assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, c);
#[extrinsic_call]
_(frame_system::Origin::<T>::Root, c);
assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, 0);
}
#[benchmark]
fn force_process_hrmp_close(
c: Linear<0, MAX_UNIQUE_CHANNELS>,
) {
for id in 0..c {
let _ = establish_para_connection::<T>(
PREFIX_0 + id,
PREFIX_1 + id,
ParachainSetupStep::CloseRequested,
);
}
assert_eq!(HrmpCloseChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, c);
#[extrinsic_call]
_(frame_system::Origin::<T>::Root, c);
assert_eq!(HrmpCloseChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, 0);
}
#[benchmark]
fn hrmp_cancel_open_request(
c: Linear<0, MAX_UNIQUE_CHANNELS>,
) {
for id in 0..c {
let _ = establish_para_connection::<T>(
PREFIX_0 + id,
PREFIX_1 + id,
ParachainSetupStep::Requested,
);
}
let [(sender, sender_origin), (recipient, _)] =
establish_para_connection::<T>(1, 2, ParachainSetupStep::Requested);
assert_eq!(
HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32,
c + 1
);
let channel_id = HrmpChannelId { sender, recipient };
#[extrinsic_call]
_(sender_origin, channel_id, c + 1);
assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, c);
}
#[benchmark]
fn clean_open_channel_requests(c: Linear<0, MAX_UNIQUE_CHANNELS>) {
for id in 0..c {
let _ = establish_para_connection::<T>(
PREFIX_0 + id,
PREFIX_1 + id,
ParachainSetupStep::Requested,
);
}
assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, c);
let outgoing = (0..c).map(|id| (id + PREFIX_1).into()).collect::<Vec<ParaId>>();
let config = configuration::ActiveConfig::<T>::get();
#[block]
{
Hrmp::<T>::clean_open_channel_requests(&config, &outgoing);
}
assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, 0);
}
#[benchmark]
fn force_open_hrmp_channel(
c: Linear<0, 1>,
) {
let sender_id: ParaId = 1u32.into();
let sender_origin: crate::Origin = 1u32.into();
let recipient_id: ParaId = 2u32.into();
let ed = T::Currency::minimum_balance();
let sender_deposit: BalanceOf<T> = configuration::ActiveConfig::<T>::get()
.hrmp_sender_deposit
.unique_saturated_into();
register_parachain_with_balance::<T>(sender_id, sender_deposit + ed);
register_parachain_with_balance::<T>(recipient_id, Zero::zero());
let capacity = configuration::ActiveConfig::<T>::get().hrmp_channel_max_capacity;
let message_size = configuration::ActiveConfig::<T>::get().hrmp_channel_max_message_size;
let channel_id = HrmpChannelId { sender: sender_id, recipient: recipient_id };
if c == 1 {
assert_ok!(Hrmp::<T>::hrmp_init_open_channel(
sender_origin.clone().into(),
recipient_id,
capacity,
message_size
));
assert!(HrmpOpenChannelRequests::<T>::get(&channel_id).is_some());
} else {
if HrmpOpenChannelRequests::<T>::get(&channel_id).is_some() {
assert_ok!(Hrmp::<T>::hrmp_cancel_open_request(
sender_origin.clone().into(),
channel_id.clone(),
MAX_UNIQUE_CHANNELS,
));
}
assert!(HrmpOpenChannelRequests::<T>::get(&channel_id).is_none());
}
assert!(HrmpChannels::<T>::get(&channel_id).is_none());
#[extrinsic_call]
_(frame_system::Origin::<T>::Root, sender_id, recipient_id, capacity, message_size);
assert_last_event::<T>(
Event::<T>::HrmpChannelForceOpened {
sender: sender_id,
recipient: recipient_id,
proposed_max_capacity: capacity,
proposed_max_message_size: message_size,
}
.into(),
);
}
#[benchmark]
fn establish_system_channel() {
let sender_id: ParaId = 1u32.into();
let recipient_id: ParaId = 2u32.into();
let caller: T::AccountId = whitelisted_caller();
let config = configuration::ActiveConfig::<T>::get();
register_parachain_with_balance::<T>(sender_id, Zero::zero());
register_parachain_with_balance::<T>(recipient_id, Zero::zero());
let capacity = config.hrmp_channel_max_capacity;
let message_size = config.hrmp_channel_max_message_size;
#[extrinsic_call]
_(frame_system::RawOrigin::Signed(caller), sender_id, recipient_id);
assert_last_event::<T>(
Event::<T>::HrmpSystemChannelOpened {
sender: sender_id,
recipient: recipient_id,
proposed_max_capacity: capacity,
proposed_max_message_size: message_size,
}
.into(),
);
}
#[benchmark]
fn poke_channel_deposits() {
let sender_id: ParaId = 1u32.into();
let recipient_id: ParaId = 2u32.into();
let channel_id = HrmpChannelId { sender: sender_id, recipient: recipient_id };
let caller: T::AccountId = whitelisted_caller();
let config = configuration::ActiveConfig::<T>::get();
let ed = T::Currency::minimum_balance();
let sender_deposit: BalanceOf<T> = config.hrmp_sender_deposit.unique_saturated_into();
let recipient_deposit: BalanceOf<T> = config.hrmp_recipient_deposit.unique_saturated_into();
register_parachain_with_balance::<T>(sender_id, ed + sender_deposit);
register_parachain_with_balance::<T>(recipient_id, ed + recipient_deposit);
HrmpChannels::<T>::insert(
&channel_id,
HrmpChannel {
sender_deposit: config.hrmp_sender_deposit,
recipient_deposit: config.hrmp_recipient_deposit,
max_capacity: config.hrmp_channel_max_capacity,
max_total_size: config.hrmp_channel_max_total_size,
max_message_size: config.hrmp_channel_max_message_size,
msg_count: 0,
total_size: 0,
mqc_head: None,
},
);
let _ = T::Currency::reserve(&sender_id.into_account_truncating(), sender_deposit);
let _ = T::Currency::reserve(&recipient_id.into_account_truncating(), recipient_deposit);
#[extrinsic_call]
_(frame_system::RawOrigin::Signed(caller), sender_id, recipient_id);
assert_last_event::<T>(
Event::<T>::OpenChannelDepositsUpdated { sender: sender_id, recipient: recipient_id }
.into(),
);
let channel = HrmpChannels::<T>::get(&channel_id).unwrap();
assert_eq!(channel.sender_deposit, 0);
assert_eq!(channel.recipient_deposit, 0);
assert_eq!(
T::Currency::reserved_balance(&sender_id.into_account_truncating()),
0u128.unique_saturated_into()
);
assert_eq!(
T::Currency::reserved_balance(&recipient_id.into_account_truncating()),
0u128.unique_saturated_into()
);
}
#[benchmark]
fn establish_channel_with_system() {
let sender_id = 1u32;
let recipient_id: ParaId = 2u32.into();
let sender_origin: crate::Origin = sender_id.into();
register_parachain_with_balance::<T>(sender_id.into(), Zero::zero());
register_parachain_with_balance::<T>(recipient_id, Zero::zero());
#[extrinsic_call]
_(sender_origin, recipient_id);
let (max_message_size, max_capacity) = T::DefaultChannelSizeAndCapacityWithSystem::get();
assert_has_event::<T>(
Event::<T>::HrmpSystemChannelOpened {
sender: sender_id.into(),
recipient: recipient_id,
proposed_max_capacity: max_capacity,
proposed_max_message_size: max_message_size,
}
.into(),
);
assert_has_event::<T>(
Event::<T>::HrmpSystemChannelOpened {
sender: recipient_id,
recipient: sender_id.into(),
proposed_max_capacity: max_capacity,
proposed_max_message_size: max_message_size,
}
.into(),
);
}
impl_benchmark_test_suite!(
Hrmp,
crate::mock::new_test_ext(crate::hrmp::tests::GenesisConfigBuilder::default().build()),
crate::mock::Test
);
}