1use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
20use codec::Encode;
21use cumulus_primitives_core::XcmpMessageSource;
22use frame_support::{
23 assert_ok,
24 traits::{Currency, Get, OnFinalize, OnInitialize, OriginTrait, ProcessMessageError},
25};
26use frame_system::pallet_prelude::BlockNumberFor;
27use parachains_common::{AccountId, Balance};
28use parachains_runtimes_test_utils::{
29 mock_open_hrmp_channel, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, RuntimeHelper,
30 SlotDurations, ValidatorIdOf, XcmReceivedFrom,
31};
32use sp_runtime::{traits::StaticLookup, Saturating};
33use xcm::{latest::prelude::*, VersionedAssets};
34use xcm_builder::{CreateMatcher, MatchXcm};
35use xcm_executor::{traits::ConvertLocation, XcmExecutor};
36
37pub struct TestBridgingConfig {
38 pub bridged_network: NetworkId,
39 pub local_bridge_hub_para_id: u32,
40 pub local_bridge_hub_location: Location,
41 pub bridged_target_location: Location,
42}
43
44pub fn limited_reserve_transfer_assets_for_native_asset_works<
46 Runtime,
47 AllPalletsWithoutSystem,
48 XcmConfig,
49 HrmpChannelOpener,
50 HrmpChannelSource,
51 LocationToAccountId,
52>(
53 collator_session_keys: CollatorSessionKeys<Runtime>,
54 slot_durations: SlotDurations,
55 existential_deposit: BalanceOf<Runtime>,
56 alice_account: AccountIdOf<Runtime>,
57 unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
58 unwrap_xcmp_queue_event: Box<
59 dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
60 >,
61 prepare_configuration: fn() -> TestBridgingConfig,
62 weight_limit: WeightLimit,
63 maybe_paid_export_message: Option<AssetId>,
64 delivery_fees_account: Option<AccountIdOf<Runtime>>,
65) where
66 Runtime: frame_system::Config
67 + pallet_balances::Config
68 + pallet_session::Config
69 + pallet_xcm::Config
70 + parachain_info::Config
71 + pallet_collator_selection::Config
72 + cumulus_pallet_parachain_system::Config
73 + cumulus_pallet_xcmp_queue::Config
74 + pallet_timestamp::Config,
75 AllPalletsWithoutSystem:
76 OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
77 AccountIdOf<Runtime>: Into<[u8; 32]>,
78 ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
79 BalanceOf<Runtime>: From<Balance>,
80 <Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
81 XcmConfig: xcm_executor::Config,
82 LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
83 <Runtime as frame_system::Config>::AccountId:
84 Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
85 <<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
86 From<<Runtime as frame_system::Config>::AccountId>,
87 <Runtime as frame_system::Config>::AccountId: From<AccountId>,
88 HrmpChannelOpener: frame_support::inherent::ProvideInherent<
89 Call = cumulus_pallet_parachain_system::Call<Runtime>,
90 >,
91 HrmpChannelSource: XcmpMessageSource,
92{
93 let runtime_para_id = 1000;
94 ExtBuilder::<Runtime>::default()
95 .with_collators(collator_session_keys.collators())
96 .with_session_keys(collator_session_keys.session_keys())
97 .with_tracing()
98 .with_safe_xcm_version(3)
99 .with_para_id(runtime_para_id.into())
100 .build()
101 .execute_with(|| {
102 let mut alice = [0u8; 32];
103 alice[0] = 1;
104 let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
105 2,
106 AccountId::from(alice).into(),
107 );
108
109 let TestBridgingConfig {
111 bridged_network,
112 local_bridge_hub_para_id,
113 bridged_target_location: target_location_from_different_consensus,
114 ..
115 } = prepare_configuration();
116
117 let reserve_account =
118 LocationToAccountId::convert_location(&target_location_from_different_consensus)
119 .expect("Sovereign account for reserves");
120 let balance_to_transfer = 1_000_000_000_000_u128;
121 let native_asset = Location::parent();
122
123 mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
125 runtime_para_id.into(),
126 local_bridge_hub_para_id.into(),
127 included_head,
128 &alice,
129 &slot_durations,
130 );
131
132 let delivery_fees_buffer = 8_000_000_000_000u128;
136 let alice_account_init_balance =
138 existential_deposit + balance_to_transfer.into() + delivery_fees_buffer.into();
139 let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
140 &alice_account,
141 alice_account_init_balance,
142 );
143 let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
145 &reserve_account,
146 existential_deposit,
147 );
148
149 assert!(
152 (<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account) -
153 balance_to_transfer.into()) >=
154 existential_deposit
155 );
156 assert_eq!(
158 <pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
159 existential_deposit
160 );
161
162 let delivery_fees_account_balance_before = delivery_fees_account
163 .as_ref()
164 .map(|dfa| <pallet_balances::Pallet<Runtime>>::free_balance(dfa))
165 .unwrap_or(0.into());
166
167 let asset_to_transfer =
169 Asset { fun: Fungible(balance_to_transfer.into()), id: native_asset.into() };
170
171 let target_destination_account = Location::new(
173 0,
174 [AccountId32 {
175 network: Some(bridged_network),
176 id: sp_runtime::AccountId32::new([3; 32]).into(),
177 }],
178 );
179
180 let assets_to_transfer = Assets::from(asset_to_transfer);
181 let mut expected_assets = assets_to_transfer.clone();
182 let context = XcmConfig::UniversalLocation::get();
183 expected_assets
184 .reanchor(&target_location_from_different_consensus, &context)
185 .unwrap();
186
187 let expected_beneficiary = target_destination_account.clone();
188
189 assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
191 RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
192 Box::new(target_location_from_different_consensus.clone().into_versioned()),
193 Box::new(target_destination_account.into_versioned()),
194 Box::new(VersionedAssets::from(assets_to_transfer)),
195 0,
196 weight_limit,
197 ));
198
199 RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::assert_pallet_xcm_event_outcome(
202 &unwrap_pallet_xcm_event,
203 |outcome| {
204 assert_ok!(outcome.ensure_complete());
205 },
206 );
207
208 let xcm_sent_message_hash = <frame_system::Pallet<Runtime>>::events()
210 .into_iter()
211 .filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
212 .find_map(|e| match e {
213 cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
214 Some(message_hash),
215 _ => None,
216 });
217
218 let xcm_sent = RuntimeHelper::<HrmpChannelSource, AllPalletsWithoutSystem>::take_xcm(
220 local_bridge_hub_para_id.into(),
221 )
222 .unwrap();
223 assert_eq!(
224 xcm_sent_message_hash,
225 Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256))
226 );
227 let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm");
228
229 let mut delivery_fees = 0;
232 if let Some(expected_fee_asset_id) = maybe_paid_export_message {
234 xcm_sent
235 .0
236 .matcher()
237 .match_next_inst(|instr| match instr {
238 WithdrawAsset(_) => Ok(()),
239 _ => Err(ProcessMessageError::BadFormat),
240 })
241 .expect("contains WithdrawAsset")
242 .match_next_inst(|instr| match instr {
243 BuyExecution { fees, .. } if fees.id.eq(&expected_fee_asset_id) => Ok(()),
244 _ => Err(ProcessMessageError::BadFormat),
245 })
246 .expect("contains BuyExecution")
247 .match_next_inst(|instr| match instr {
248 SetAppendix(_) => Ok(()),
249 _ => Err(ProcessMessageError::BadFormat),
250 })
251 .expect("contains SetAppendix")
252 } else {
253 xcm_sent
254 .0
255 .matcher()
256 .match_next_inst(|instr| match instr {
257 UnpaidExecution { weight_limit, check_origin }
260 if weight_limit == &Unlimited && check_origin.is_none() =>
261 Ok(()),
262 _ => Err(ProcessMessageError::BadFormat),
263 })
264 .expect("contains UnpaidExecution")
265 }
266 .match_next_inst(|instr| match instr {
268 ExportMessage { network, destination, xcm: inner_xcm } => {
270 assert_eq!(network, &bridged_network);
271 let (_, target_location_junctions_without_global_consensus) =
272 target_location_from_different_consensus
273 .interior
274 .clone()
275 .split_global()
276 .expect("split works");
277 assert_eq!(destination, &target_location_junctions_without_global_consensus);
278 delivery_fees = get_fungible_delivery_fees::<
280 <XcmConfig as xcm_executor::Config>::XcmSender,
281 >(
282 target_location_from_different_consensus.clone(),
283 inner_xcm.clone(),
284 );
285 assert_matches_reserve_asset_deposited_instructions(
286 inner_xcm,
287 &expected_assets,
288 &expected_beneficiary,
289 );
290 Ok(())
291 },
292 _ => Err(ProcessMessageError::BadFormat),
293 })
294 .expect("contains ExportMessage");
295
296 assert_eq!(
298 <pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
299 alice_account_init_balance
300 .saturating_sub(balance_to_transfer.into())
301 .saturating_sub(delivery_fees.into())
302 );
303
304 assert_eq!(
306 <pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
307 existential_deposit + balance_to_transfer.into()
308 );
309
310 if let Some(delivery_fees_account) = delivery_fees_account {
312 let delivery_fees_account_balance_after =
313 <pallet_balances::Pallet<Runtime>>::free_balance(&delivery_fees_account);
314 assert!(
315 delivery_fees_account_balance_after - delivery_fees.into() >=
316 delivery_fees_account_balance_before
317 );
318 }
319 })
320}
321
322pub fn receive_reserve_asset_deposited_from_different_consensus_works<
323 Runtime,
324 AllPalletsWithoutSystem,
325 XcmConfig,
326 ForeignAssetsPalletInstance,
327>(
328 collator_session_keys: CollatorSessionKeys<Runtime>,
329 existential_deposit: BalanceOf<Runtime>,
330 target_account: AccountIdOf<Runtime>,
331 block_author_account: AccountIdOf<Runtime>,
332 (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): (
333 AccountIdOf<Runtime>,
334 xcm::v5::Location,
335 u128,
336 ),
337 foreign_asset_id_amount_to_transfer: u128,
338 prepare_configuration: impl FnOnce() -> TestBridgingConfig,
339 (bridge_instance, universal_origin, descend_origin): (Junctions, Junction, Junctions), additional_checks_before: impl FnOnce(),
341 additional_checks_after: impl FnOnce(),
342) where
343 Runtime: frame_system::Config
344 + pallet_balances::Config
345 + pallet_session::Config
346 + pallet_xcm::Config
347 + parachain_info::Config
348 + pallet_collator_selection::Config
349 + cumulus_pallet_parachain_system::Config
350 + cumulus_pallet_xcmp_queue::Config
351 + pallet_assets::Config<ForeignAssetsPalletInstance>
352 + pallet_timestamp::Config,
353 AllPalletsWithoutSystem:
354 OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
355 AccountIdOf<Runtime>: Into<[u8; 32]> + From<[u8; 32]>,
356 ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
357 BalanceOf<Runtime>: From<Balance> + Into<Balance>,
358 XcmConfig: xcm_executor::Config,
359 <Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetId:
360 From<xcm::v5::Location> + Into<xcm::v5::Location>,
361 <Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetIdParameter:
362 From<xcm::v5::Location> + Into<xcm::v5::Location>,
363 <Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::Balance:
364 From<Balance> + Into<u128> + From<u128>,
365 <Runtime as frame_system::Config>::AccountId: Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>
366 + Into<AccountId>,
367 <<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
368 From<<Runtime as frame_system::Config>::AccountId>,
369 ForeignAssetsPalletInstance: 'static,
370{
371 ExtBuilder::<Runtime>::default()
372 .with_collators(collator_session_keys.collators())
373 .with_session_keys(collator_session_keys.session_keys())
374 .with_tracing()
375 .build()
376 .execute_with(|| {
377 RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
379 2,
380 block_author_account.clone().into(),
381 );
382
383 let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
385 &target_account,
386 existential_deposit,
387 );
388
389 assert_ok!(
391 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::force_create(
392 RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::root_origin(),
393 foreign_asset_id_location.clone().into(),
394 foreign_asset_owner.into(),
395 true, foreign_asset_id_minimum_balance.into()
397 )
398 );
399
400 let TestBridgingConfig { local_bridge_hub_location, .. } = prepare_configuration();
402
403 assert_eq!(
405 <pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
406 existential_deposit.clone()
407 );
408
409 assert_eq!(
411 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
412 foreign_asset_id_location.clone().into(),
413 &target_account
414 ),
415 0.into()
416 );
417
418 additional_checks_before();
420
421 let expected_assets = Assets::from(vec![Asset {
422 id: AssetId(foreign_asset_id_location.clone()),
423 fun: Fungible(foreign_asset_id_amount_to_transfer),
424 }]);
425 let expected_beneficiary = Location::new(
426 0,
427 [AccountId32 { network: None, id: target_account.clone().into() }],
428 );
429
430 let xcm = Xcm(vec![
432 DescendOrigin(bridge_instance),
433 UniversalOrigin(universal_origin),
434 DescendOrigin(descend_origin),
435 ReserveAssetDeposited(expected_assets.clone()),
436 ClearOrigin,
437 BuyExecution {
438 fees: Asset {
439 id: AssetId(foreign_asset_id_location.clone()),
440 fun: Fungible(foreign_asset_id_amount_to_transfer),
441 },
442 weight_limit: Unlimited,
443 },
444 DepositAsset {
445 assets: Wild(AllCounted(1)),
446 beneficiary: expected_beneficiary.clone(),
447 },
448 SetTopic([
449 220, 188, 144, 32, 213, 83, 111, 175, 44, 210, 111, 19, 90, 165, 191, 112, 140,
450 247, 192, 124, 42, 17, 153, 141, 114, 34, 189, 20, 83, 69, 237, 173,
451 ]),
452 ]);
453 assert_matches_reserve_asset_deposited_instructions(
454 &mut xcm.clone(),
455 &expected_assets,
456 &expected_beneficiary,
457 );
458
459 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
460
461 let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
463 local_bridge_hub_location,
464 xcm,
465 &mut hash,
466 RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::xcm_max_weight(
467 XcmReceivedFrom::Sibling,
468 ),
469 Weight::zero(),
470 );
471 assert_ok!(outcome.ensure_complete());
472
473 assert_eq!(
475 <pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
476 existential_deposit.clone()
477 );
478
479 assert!(
481 <pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
482 foreign_asset_id_location.into(),
483 &target_account
484 ) > 0.into()
485 );
486
487 additional_checks_after();
489 })
490}
491
492pub fn report_bridge_status_from_xcm_bridge_router_works<
493 Runtime,
494 AllPalletsWithoutSystem,
495 XcmConfig,
496 LocationToAccountId,
497 XcmBridgeHubRouterInstance,
498>(
499 collator_session_keys: CollatorSessionKeys<Runtime>,
500 prepare_configuration: fn() -> TestBridgingConfig,
501 congested_message: fn() -> Xcm<XcmConfig::RuntimeCall>,
502 uncongested_message: fn() -> Xcm<XcmConfig::RuntimeCall>,
503) where
504 Runtime: frame_system::Config
505 + pallet_balances::Config
506 + pallet_session::Config
507 + pallet_xcm::Config
508 + parachain_info::Config
509 + pallet_collator_selection::Config
510 + cumulus_pallet_parachain_system::Config
511 + cumulus_pallet_xcmp_queue::Config
512 + pallet_xcm_bridge_hub_router::Config<XcmBridgeHubRouterInstance>
513 + pallet_timestamp::Config,
514 AllPalletsWithoutSystem:
515 OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
516 AccountIdOf<Runtime>: Into<[u8; 32]>,
517 ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
518 BalanceOf<Runtime>: From<Balance>,
519 <Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
520 XcmConfig: xcm_executor::Config,
521 LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
522 <Runtime as frame_system::Config>::AccountId:
523 Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
524 <<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
525 From<<Runtime as frame_system::Config>::AccountId>,
526 <Runtime as frame_system::Config>::AccountId: From<AccountId>,
527 XcmBridgeHubRouterInstance: 'static,
528{
529 ExtBuilder::<Runtime>::default()
530 .with_collators(collator_session_keys.collators())
531 .with_session_keys(collator_session_keys.session_keys())
532 .with_tracing()
533 .build()
534 .execute_with(|| {
535 let report_bridge_status = |is_congested: bool| {
536 let TestBridgingConfig { local_bridge_hub_location, .. } = prepare_configuration();
538
539 let xcm = if is_congested { congested_message() } else { uncongested_message() };
541 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
542
543 let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
545 local_bridge_hub_location.clone(),
546 xcm,
547 &mut hash,
548 RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::xcm_max_weight(
549 XcmReceivedFrom::Sibling,
550 ),
551 Weight::zero(),
552 );
553 assert_ok!(outcome.ensure_complete());
554 assert_eq!(is_congested, pallet_xcm_bridge_hub_router::Pallet::<Runtime, XcmBridgeHubRouterInstance>::bridge().is_congested);
555 };
556
557 report_bridge_status(true);
558 report_bridge_status(false);
559 })
560}