1#![cfg(feature = "runtime-benchmarks")]
18
19use crate::{
20 configuration::Pallet as Configuration,
21 hrmp::{Pallet as Hrmp, *},
22 paras::{Pallet as Paras, ParaKind, ParachainsCache},
23 shared::Pallet as Shared,
24};
25use frame_benchmarking::{v2::*, whitelisted_caller};
26use frame_support::{assert_ok, traits::Currency};
27
28type BalanceOf<T> =
29 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
30
31fn register_parachain_with_balance<T: Config>(id: ParaId, balance: BalanceOf<T>) {
32 let mut parachains = ParachainsCache::new();
33 Paras::<T>::initialize_para_now(
34 &mut parachains,
35 id,
36 &crate::paras::ParaGenesisArgs {
37 para_kind: ParaKind::Parachain,
38 genesis_head: vec![1].into(),
39 validation_code: vec![1].into(),
40 },
41 );
42 T::Currency::make_free_balance_be(&id.into_account_truncating(), balance);
43}
44
45fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
46 let events = frame_system::Pallet::<T>::events();
47 let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
48 let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
50 assert_eq!(event, &system_event);
51}
52
53fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
54 let events = frame_system::Pallet::<T>::events();
55 let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
56
57 assert!(events.iter().any(|record| record.event == system_event));
58}
59
60enum ParachainSetupStep {
62 Requested,
64 Accepted,
66 Established,
68 CloseRequested,
71}
72
73fn establish_para_connection<T: Config>(
74 from: u32,
75 to: u32,
76 until: ParachainSetupStep,
77) -> [(ParaId, crate::Origin); 2]
78where
79 <T as frame_system::Config>::RuntimeOrigin: From<crate::Origin>,
80{
81 let config = configuration::ActiveConfig::<T>::get();
82 let ed = T::Currency::minimum_balance();
83 let deposit: BalanceOf<T> = config.hrmp_sender_deposit.unique_saturated_into();
84 let capacity = config.hrmp_channel_max_capacity;
85 let message_size = config.hrmp_channel_max_message_size;
86 assert!(message_size > 0, "Invalid genesis for benchmarking");
87 assert!(capacity > 0, "Invalid genesis for benchmarking");
88
89 let sender: ParaId = from.into();
90 let sender_origin: crate::Origin = from.into();
91
92 let recipient: ParaId = to.into();
93 let recipient_origin: crate::Origin = to.into();
94
95 let output = [(sender, sender_origin.clone()), (recipient, recipient_origin.clone())];
96
97 if !Paras::<T>::is_parachain(sender) {
99 register_parachain_with_balance::<T>(sender, deposit + ed);
100 }
101 if !Paras::<T>::is_parachain(recipient) {
102 register_parachain_with_balance::<T>(recipient, deposit + ed);
103 }
104
105 assert_ok!(Hrmp::<T>::hrmp_init_open_channel(
106 sender_origin.clone().into(),
107 recipient,
108 capacity,
109 message_size
110 ));
111
112 if matches!(until, ParachainSetupStep::Requested) {
113 return output
114 }
115
116 assert_ok!(Hrmp::<T>::hrmp_accept_open_channel(recipient_origin.into(), sender));
117 if matches!(until, ParachainSetupStep::Accepted) {
118 return output
119 }
120
121 Hrmp::<T>::process_hrmp_open_channel_requests(&configuration::ActiveConfig::<T>::get());
122 if matches!(until, ParachainSetupStep::Established) {
123 return output
124 }
125
126 let channel_id = HrmpChannelId { sender, recipient };
127 assert_ok!(Hrmp::<T>::hrmp_close_channel(sender_origin.clone().into(), channel_id));
128 if matches!(until, ParachainSetupStep::CloseRequested) {
129 return output
131 }
132
133 output
134}
135
136const PREFIX_0: u32 = 10_000;
146const PREFIX_1: u32 = PREFIX_0 * 2;
147const MAX_UNIQUE_CHANNELS: u32 = 128;
148
149static_assertions::const_assert!(MAX_UNIQUE_CHANNELS < PREFIX_0);
150static_assertions::const_assert!(HRMP_MAX_INBOUND_CHANNELS_BOUND < PREFIX_0);
151static_assertions::const_assert!(HRMP_MAX_OUTBOUND_CHANNELS_BOUND < PREFIX_0);
152
153#[benchmarks(where <T as frame_system::Config>::RuntimeOrigin: From<crate::Origin>)]
154mod benchmarks {
155 use super::*;
156
157 #[benchmark]
158 fn hrmp_init_open_channel() {
159 let sender_id: ParaId = 1u32.into();
160 let sender_origin: crate::Origin = 1u32.into();
161
162 let recipient_id: ParaId = 2u32.into();
163
164 let ed = T::Currency::minimum_balance();
166 let deposit: BalanceOf<T> = configuration::ActiveConfig::<T>::get()
167 .hrmp_sender_deposit
168 .unique_saturated_into();
169 register_parachain_with_balance::<T>(sender_id, deposit + ed);
170 register_parachain_with_balance::<T>(recipient_id, deposit + ed);
171
172 let capacity = configuration::ActiveConfig::<T>::get().hrmp_channel_max_capacity;
173 let message_size = configuration::ActiveConfig::<T>::get().hrmp_channel_max_message_size;
174
175 #[extrinsic_call]
176 _(sender_origin, recipient_id, capacity, message_size);
177
178 assert_last_event::<T>(
179 Event::<T>::OpenChannelRequested {
180 sender: sender_id,
181 recipient: recipient_id,
182 proposed_max_capacity: capacity,
183 proposed_max_message_size: message_size,
184 }
185 .into(),
186 );
187 }
188
189 #[benchmark]
190 fn hrmp_accept_open_channel() {
191 let [(sender, _), (recipient, recipient_origin)] =
192 establish_para_connection::<T>(1, 2, ParachainSetupStep::Requested);
193
194 #[extrinsic_call]
195 _(recipient_origin, sender);
196
197 assert_last_event::<T>(Event::<T>::OpenChannelAccepted { sender, recipient }.into());
198 }
199
200 #[benchmark]
201 fn hrmp_close_channel() {
202 let [(sender, sender_origin), (recipient, _)] =
203 establish_para_connection::<T>(1, 2, ParachainSetupStep::Established);
204 let channel_id = HrmpChannelId { sender, recipient };
205
206 #[extrinsic_call]
207 _(sender_origin, channel_id.clone());
208
209 assert_last_event::<T>(
210 Event::<T>::ChannelClosed { by_parachain: sender, channel_id }.into(),
211 );
212 }
213
214 #[benchmark]
217 fn force_clean_hrmp(
218 i: Linear<0, { HRMP_MAX_INBOUND_CHANNELS_BOUND - 1 }>,
220 e: Linear<0, { HRMP_MAX_OUTBOUND_CHANNELS_BOUND - 1 }>,
222 ) {
223 assert_ok!(Configuration::<T>::set_hrmp_max_parachain_outbound_channels(
225 frame_system::RawOrigin::Root.into(),
226 e + 1
227 ));
228 assert_ok!(Configuration::<T>::set_hrmp_max_parachain_inbound_channels(
229 frame_system::RawOrigin::Root.into(),
230 i + 1
231 ));
232 assert_ok!(Configuration::<T>::set_max_downward_message_size(
233 frame_system::RawOrigin::Root.into(),
234 1024
235 ));
236 Configuration::<T>::initializer_on_new_session(&Shared::<T>::scheduled_session());
238
239 let config = configuration::ActiveConfig::<T>::get();
240 let deposit: BalanceOf<T> = config.hrmp_sender_deposit.unique_saturated_into();
241
242 let para: ParaId = 1u32.into();
243 register_parachain_with_balance::<T>(para, deposit);
244 T::Currency::make_free_balance_be(¶.into_account_truncating(), deposit * 256u32.into());
245
246 for ingress_para_id in 0..i {
247 let ingress_para_id = ingress_para_id + PREFIX_0;
249 let _ = establish_para_connection::<T>(
250 ingress_para_id,
251 para.into(),
252 ParachainSetupStep::Established,
253 );
254 }
255
256 assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default(), 0);
258
259 for egress_para_id in 0..e {
260 let egress_para_id = egress_para_id + PREFIX_1;
262 let _ = establish_para_connection::<T>(
263 para.into(),
264 egress_para_id,
265 ParachainSetupStep::Established,
266 );
267 }
268
269 assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default(), 0);
271
272 assert_eq!(HrmpChannels::<T>::iter().count() as u32, i + e);
274
275 #[extrinsic_call]
276 _(frame_system::Origin::<T>::Root, para, i, e);
277
278 assert_eq!(HrmpChannels::<T>::iter().count() as u32, 0);
280 Hrmp::<T>::assert_storage_consistency_exhaustive();
283 }
284
285 #[benchmark]
286 fn force_process_hrmp_open(
287 c: Linear<0, MAX_UNIQUE_CHANNELS>,
290 ) {
291 for id in 0..c {
292 let _ = establish_para_connection::<T>(
293 PREFIX_0 + id,
294 PREFIX_1 + id,
295 ParachainSetupStep::Accepted,
296 );
297 }
298 assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, c);
299
300 #[extrinsic_call]
301 _(frame_system::Origin::<T>::Root, c);
302
303 assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, 0);
304 }
305
306 #[benchmark]
307 fn force_process_hrmp_close(
308 c: Linear<0, MAX_UNIQUE_CHANNELS>,
311 ) {
312 for id in 0..c {
313 let _ = establish_para_connection::<T>(
314 PREFIX_0 + id,
315 PREFIX_1 + id,
316 ParachainSetupStep::CloseRequested,
317 );
318 }
319
320 assert_eq!(HrmpCloseChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, c);
321
322 #[extrinsic_call]
323 _(frame_system::Origin::<T>::Root, c);
324
325 assert_eq!(HrmpCloseChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, 0);
326 }
327
328 #[benchmark]
329 fn hrmp_cancel_open_request(
330 c: Linear<0, MAX_UNIQUE_CHANNELS>,
333 ) {
334 for id in 0..c {
335 let _ = establish_para_connection::<T>(
336 PREFIX_0 + id,
337 PREFIX_1 + id,
338 ParachainSetupStep::Requested,
339 );
340 }
341
342 let [(sender, sender_origin), (recipient, _)] =
343 establish_para_connection::<T>(1, 2, ParachainSetupStep::Requested);
344 assert_eq!(
345 HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32,
346 c + 1
347 );
348 let channel_id = HrmpChannelId { sender, recipient };
349
350 #[extrinsic_call]
351 _(sender_origin, channel_id, c + 1);
352
353 assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, c);
354 }
355
356 #[benchmark]
360 fn clean_open_channel_requests(c: Linear<0, MAX_UNIQUE_CHANNELS>) {
361 for id in 0..c {
362 let _ = establish_para_connection::<T>(
363 PREFIX_0 + id,
364 PREFIX_1 + id,
365 ParachainSetupStep::Requested,
366 );
367 }
368
369 assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, c);
370 let outgoing = (0..c).map(|id| (id + PREFIX_1).into()).collect::<Vec<ParaId>>();
371 let config = configuration::ActiveConfig::<T>::get();
372
373 #[block]
374 {
375 Hrmp::<T>::clean_open_channel_requests(&config, &outgoing);
376 }
377
378 assert_eq!(HrmpOpenChannelRequestsList::<T>::decode_len().unwrap_or_default() as u32, 0);
379 }
380
381 #[benchmark]
382 fn force_open_hrmp_channel(
383 c: Linear<0, 1>,
386 ) {
387 let sender_id: ParaId = 1u32.into();
388 let sender_origin: crate::Origin = 1u32.into();
389 let recipient_id: ParaId = 2u32.into();
390
391 let ed = T::Currency::minimum_balance();
394 let sender_deposit: BalanceOf<T> = configuration::ActiveConfig::<T>::get()
395 .hrmp_sender_deposit
396 .unique_saturated_into();
397 register_parachain_with_balance::<T>(sender_id, sender_deposit + ed);
398 register_parachain_with_balance::<T>(recipient_id, Zero::zero());
399
400 let capacity = configuration::ActiveConfig::<T>::get().hrmp_channel_max_capacity;
401 let message_size = configuration::ActiveConfig::<T>::get().hrmp_channel_max_message_size;
402
403 let channel_id = HrmpChannelId { sender: sender_id, recipient: recipient_id };
404 if c == 1 {
405 assert_ok!(Hrmp::<T>::hrmp_init_open_channel(
408 sender_origin.clone().into(),
409 recipient_id,
410 capacity,
411 message_size
412 ));
413 assert!(HrmpOpenChannelRequests::<T>::get(&channel_id).is_some());
414 } else {
415 if HrmpOpenChannelRequests::<T>::get(&channel_id).is_some() {
416 assert_ok!(Hrmp::<T>::hrmp_cancel_open_request(
417 sender_origin.clone().into(),
418 channel_id.clone(),
419 MAX_UNIQUE_CHANNELS,
420 ));
421 }
422 assert!(HrmpOpenChannelRequests::<T>::get(&channel_id).is_none());
423 }
424
425 assert!(HrmpChannels::<T>::get(&channel_id).is_none());
427
428 #[extrinsic_call]
429 _(frame_system::Origin::<T>::Root, sender_id, recipient_id, capacity, message_size);
430
431 assert_last_event::<T>(
432 Event::<T>::HrmpChannelForceOpened {
433 sender: sender_id,
434 recipient: recipient_id,
435 proposed_max_capacity: capacity,
436 proposed_max_message_size: message_size,
437 }
438 .into(),
439 );
440 }
441
442 #[benchmark]
443 fn establish_system_channel() {
444 let sender_id: ParaId = 1u32.into();
445 let recipient_id: ParaId = 2u32.into();
446
447 let caller: T::AccountId = whitelisted_caller();
448 let config = configuration::ActiveConfig::<T>::get();
449
450 register_parachain_with_balance::<T>(sender_id, Zero::zero());
452 register_parachain_with_balance::<T>(recipient_id, Zero::zero());
453
454 let capacity = config.hrmp_channel_max_capacity;
455 let message_size = config.hrmp_channel_max_message_size;
456
457 #[extrinsic_call]
458 _(frame_system::RawOrigin::Signed(caller), sender_id, recipient_id);
459
460 assert_last_event::<T>(
461 Event::<T>::HrmpSystemChannelOpened {
462 sender: sender_id,
463 recipient: recipient_id,
464 proposed_max_capacity: capacity,
465 proposed_max_message_size: message_size,
466 }
467 .into(),
468 );
469 }
470
471 #[benchmark]
472 fn poke_channel_deposits() {
473 let sender_id: ParaId = 1u32.into();
474 let recipient_id: ParaId = 2u32.into();
475 let channel_id = HrmpChannelId { sender: sender_id, recipient: recipient_id };
476
477 let caller: T::AccountId = whitelisted_caller();
478 let config = configuration::ActiveConfig::<T>::get();
479
480 let ed = T::Currency::minimum_balance();
482 let sender_deposit: BalanceOf<T> = config.hrmp_sender_deposit.unique_saturated_into();
483 let recipient_deposit: BalanceOf<T> = config.hrmp_recipient_deposit.unique_saturated_into();
484 register_parachain_with_balance::<T>(sender_id, ed + sender_deposit);
485 register_parachain_with_balance::<T>(recipient_id, ed + recipient_deposit);
486
487 HrmpChannels::<T>::insert(
489 &channel_id,
490 HrmpChannel {
491 sender_deposit: config.hrmp_sender_deposit,
492 recipient_deposit: config.hrmp_recipient_deposit,
493 max_capacity: config.hrmp_channel_max_capacity,
494 max_total_size: config.hrmp_channel_max_total_size,
495 max_message_size: config.hrmp_channel_max_message_size,
496 msg_count: 0,
497 total_size: 0,
498 mqc_head: None,
499 },
500 );
501 let _ = T::Currency::reserve(&sender_id.into_account_truncating(), sender_deposit);
503 let _ = T::Currency::reserve(&recipient_id.into_account_truncating(), recipient_deposit);
504
505 #[extrinsic_call]
506 _(frame_system::RawOrigin::Signed(caller), sender_id, recipient_id);
507
508 assert_last_event::<T>(
509 Event::<T>::OpenChannelDepositsUpdated { sender: sender_id, recipient: recipient_id }
510 .into(),
511 );
512 let channel = HrmpChannels::<T>::get(&channel_id).unwrap();
513 assert_eq!(channel.sender_deposit, 0);
515 assert_eq!(channel.recipient_deposit, 0);
516 assert_eq!(
518 T::Currency::reserved_balance(&sender_id.into_account_truncating()),
519 0u128.unique_saturated_into()
520 );
521 assert_eq!(
522 T::Currency::reserved_balance(&recipient_id.into_account_truncating()),
523 0u128.unique_saturated_into()
524 );
525 }
526
527 #[benchmark]
528 fn establish_channel_with_system() {
529 let sender_id = 1u32;
530 let recipient_id: ParaId = 2u32.into();
531
532 let sender_origin: crate::Origin = sender_id.into();
533
534 register_parachain_with_balance::<T>(sender_id.into(), Zero::zero());
536 register_parachain_with_balance::<T>(recipient_id, Zero::zero());
537
538 #[extrinsic_call]
539 _(sender_origin, recipient_id);
540
541 let (max_message_size, max_capacity) = T::DefaultChannelSizeAndCapacityWithSystem::get();
542
543 assert_has_event::<T>(
544 Event::<T>::HrmpSystemChannelOpened {
545 sender: sender_id.into(),
546 recipient: recipient_id,
547 proposed_max_capacity: max_capacity,
548 proposed_max_message_size: max_message_size,
549 }
550 .into(),
551 );
552
553 assert_has_event::<T>(
554 Event::<T>::HrmpSystemChannelOpened {
555 sender: recipient_id,
556 recipient: sender_id.into(),
557 proposed_max_capacity: max_capacity,
558 proposed_max_message_size: max_message_size,
559 }
560 .into(),
561 );
562 }
563
564 impl_benchmark_test_suite!(
565 Hrmp,
566 crate::mock::new_test_ext(crate::hrmp::tests::GenesisConfigBuilder::default().build()),
567 crate::mock::Test
568 );
569}