xcm_executor_integration_tests/
lib.rs1#![cfg(test)]
18
19use codec::Encode;
20use frame_support::weights::Weight;
21use polkadot_test_client::{
22 BlockBuilderExt, ClientBlockImportExt, DefaultTestClientBuilderExt, InitPolkadotBlockBuilder,
23 TestClientBuilder, TestClientBuilderExt,
24};
25use polkadot_test_runtime::{pallet_test_notifier, xcm_config::XcmConfig};
26use polkadot_test_service::construct_extrinsic;
27use sp_runtime::traits::Block;
28use sp_state_machine::InspectState;
29use xcm::{latest::prelude::*, VersionedResponse, VersionedXcm};
30use xcm_executor::traits::WeightBounds;
31
32#[test]
33fn basic_buy_fees_message_executes() {
34 sp_tracing::try_init_simple();
35 let client = TestClientBuilder::new().build();
36
37 let msg = Xcm(vec![
38 WithdrawAsset((Parent, 100).into()),
39 BuyExecution { fees: (Parent, 1).into(), weight_limit: Unlimited },
40 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Parent.into() },
41 ]);
42
43 let mut block_builder = client.init_polkadot_block_builder();
44
45 let execute = construct_extrinsic(
46 &client,
47 polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute {
48 message: Box::new(VersionedXcm::from(msg)),
49 max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024),
50 }),
51 sp_keyring::Sr25519Keyring::Alice,
52 0,
53 );
54
55 block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
56
57 let block = block_builder.build().expect("Finalizes the block").block;
58 let block_hash = block.hash();
59
60 futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
61 .expect("imports the block");
62
63 client.state_at(block_hash).expect("state should exist").inspect_state(|| {
64 assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
65 r.event,
66 polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted {
67 outcome: Outcome::Complete { .. }
68 }),
69 )));
70 });
71}
72
73#[test]
74fn transact_recursion_limit_works() {
75 sp_tracing::try_init_simple();
76 let client = TestClientBuilder::new().build();
77
78 let base_xcm = |call: polkadot_test_runtime::RuntimeCall| {
79 Xcm(vec![
80 WithdrawAsset((Here, 1_000).into()),
81 BuyExecution { fees: (Here, 1).into(), weight_limit: Unlimited },
82 Transact {
83 origin_kind: OriginKind::Native,
84 call: call.encode().into(),
85 fallback_max_weight: None,
86 },
87 ])
88 };
89 let mut call: Option<polkadot_test_runtime::RuntimeCall> = None;
90 for depth in (1..12).rev() {
92 let mut msg;
93 match depth {
94 11 => {
96 msg = Xcm(vec![ClearOrigin]);
97 },
98 10 => {
101 let inner_call = call.take().unwrap();
102 let expected_transact_status =
103 sp_runtime::DispatchError::Module(sp_runtime::ModuleError {
104 index: 27,
105 error: [28, 0, 40, 0], message: Some("LocalExecutionIncompleteWithError"),
107 })
108 .encode()
109 .into();
110 msg = base_xcm(inner_call);
111 msg.inner_mut().push(ExpectTransactStatus(expected_transact_status));
112 },
113 d if d >= 1 && d <= 9 => {
115 let inner_call = call.take().unwrap();
116 msg = base_xcm(inner_call);
117 msg.inner_mut().push(ExpectTransactStatus(MaybeErrorCode::Success));
118 },
119 _ => unreachable!(),
120 }
121 let max_weight =
122 <XcmConfig as xcm_executor::Config>::Weigher::weight(&mut msg, Weight::MAX).unwrap();
123 call = Some(polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute {
124 message: Box::new(VersionedXcm::from(msg.clone())),
125 max_weight,
126 }));
127 }
128
129 let mut block_builder = client.init_polkadot_block_builder();
130
131 let execute = construct_extrinsic(&client, call.unwrap(), sp_keyring::Sr25519Keyring::Alice, 0);
132
133 block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
134
135 let block = block_builder.build().expect("Finalizes the block").block;
136 let block_hash = block.hash();
137
138 futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
139 .expect("imports the block");
140
141 client.state_at(block_hash).expect("state should exist").inspect_state(|| {
142 let events = polkadot_test_runtime::System::events();
143 assert_eq!(
145 polkadot_test_runtime::System::events()
146 .iter()
147 .filter(|r| matches!(
148 r.event,
149 polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted {
150 outcome: Outcome::Complete { .. }
151 }),
152 ))
153 .count(),
154 10
155 );
156 assert!(events.iter().any(|r| matches!(
158 &r.event,
159 polkadot_test_runtime::RuntimeEvent::TransactionPayment(
160 pallet_transaction_payment::Event::TransactionFeePaid {
161 who: payer,
162 ..
163 }
164 ) if *payer == sp_keyring::Sr25519Keyring::Alice.into(),
165 )));
166 });
167}
168
169#[test]
170fn query_response_fires() {
171 use pallet_test_notifier::Event::*;
172 use pallet_xcm::QueryStatus;
173 use polkadot_test_runtime::RuntimeEvent::TestNotifier;
174
175 sp_tracing::try_init_simple();
176 let client = TestClientBuilder::new().build();
177
178 let mut block_builder = client.init_polkadot_block_builder();
179
180 let execute = construct_extrinsic(
181 &client,
182 polkadot_test_runtime::RuntimeCall::TestNotifier(
183 pallet_test_notifier::Call::prepare_new_query {},
184 ),
185 sp_keyring::Sr25519Keyring::Alice,
186 0,
187 );
188
189 block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
190
191 let block = block_builder.build().expect("Finalizes the block").block;
192 let block_hash = block.hash();
193
194 futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
195 .expect("imports the block");
196
197 let mut query_id = None;
198 client.state_at(block_hash).expect("state should exist").inspect_state(|| {
199 for r in polkadot_test_runtime::System::events().iter() {
200 match r.event {
201 TestNotifier(QueryPrepared(q)) => query_id = Some(q),
202 _ => (),
203 }
204 }
205 });
206 let query_id = query_id.unwrap();
207
208 let mut block_builder = client.init_polkadot_block_builder();
209
210 let response = Response::ExecutionResult(None);
211 let max_weight = Weight::from_parts(1_000_000, 1024 * 1024);
212 let querier = Some(Here.into());
213 let msg = Xcm(vec![QueryResponse { query_id, response, max_weight, querier }]);
214 let msg = Box::new(VersionedXcm::from(msg));
215
216 let execute = construct_extrinsic(
217 &client,
218 polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute {
219 message: msg,
220 max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024),
221 }),
222 sp_keyring::Sr25519Keyring::Alice,
223 1,
224 );
225
226 block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
227
228 let block = block_builder.build().expect("Finalizes the block").block;
229 let block_hash = block.hash();
230
231 futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
232 .expect("imports the block");
233
234 client.state_at(block_hash).expect("state should exist").inspect_state(|| {
235 assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
236 r.event,
237 polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::ResponseReady {
238 query_id: q,
239 response: Response::ExecutionResult(None),
240 }) if q == query_id,
241 )));
242 assert_eq!(
243 polkadot_test_runtime::Xcm::query(&query_id),
244 Some(QueryStatus::Ready {
245 response: VersionedResponse::from(Response::ExecutionResult(None)),
246 at: 2u32.into()
247 }),
248 )
249 });
250}
251
252#[test]
253fn query_response_elicits_handler() {
254 use pallet_test_notifier::Event::*;
255 use polkadot_test_runtime::RuntimeEvent::TestNotifier;
256
257 sp_tracing::try_init_simple();
258 let client = TestClientBuilder::new().build();
259
260 let mut block_builder = client.init_polkadot_block_builder();
261
262 let execute = construct_extrinsic(
263 &client,
264 polkadot_test_runtime::RuntimeCall::TestNotifier(
265 pallet_test_notifier::Call::prepare_new_notify_query {},
266 ),
267 sp_keyring::Sr25519Keyring::Alice,
268 0,
269 );
270
271 block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
272
273 let block = block_builder.build().expect("Finalizes the block").block;
274 let block_hash = block.hash();
275
276 futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
277 .expect("imports the block");
278
279 let mut query_id = None;
280 client.state_at(block_hash).expect("state should exist").inspect_state(|| {
281 for r in polkadot_test_runtime::System::events().iter() {
282 match r.event {
283 TestNotifier(NotifyQueryPrepared(q)) => query_id = Some(q),
284 _ => (),
285 }
286 }
287 });
288 let query_id = query_id.unwrap();
289
290 let mut block_builder = client.init_polkadot_block_builder();
291
292 let response = Response::ExecutionResult(None);
293 let max_weight = Weight::from_parts(1_000_000, 1024 * 1024);
294 let querier = Some(Here.into());
295 let msg = Xcm(vec![QueryResponse { query_id, response, max_weight, querier }]);
296
297 let execute = construct_extrinsic(
298 &client,
299 polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute {
300 message: Box::new(VersionedXcm::from(msg)),
301 max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024),
302 }),
303 sp_keyring::Sr25519Keyring::Alice,
304 1,
305 );
306
307 block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
308
309 let block = block_builder.build().expect("Finalizes the block").block;
310 let block_hash = block.hash();
311
312 futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
313 .expect("imports the block");
314
315 client.state_at(block_hash).expect("state should exist").inspect_state(|| {
316 assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
317 &r.event,
318 TestNotifier(ResponseReceived(
319 location,
320 q,
321 Response::ExecutionResult(None),
322 )) if *q == query_id && matches!(location.unpack(), (0, [Junction::AccountId32 { .. }])),
323 )));
324 });
325}
326
327#[test]
332fn deposit_reserve_asset_works_for_any_xcm_sender() {
333 sp_tracing::try_init_simple();
334 let client = TestClientBuilder::new().build();
335
336 let amount_to_send: u128 = 1_000_000_000_000;
338 let assets: Assets = (Parent, amount_to_send).into();
339 let fee_asset_item = 0;
340 let max_assets = assets.len() as u32;
341 let fees = assets.get(fee_asset_item as usize).unwrap().clone();
342 let weight_limit = Unlimited;
343 let reserve = Location::parent();
344 let dest = Location::new(1, [Parachain(2000)]);
345 let beneficiary_id = sp_keyring::Sr25519Keyring::Alice.to_account_id();
346 let beneficiary = Location::new(0, [AccountId32 { network: None, id: beneficiary_id.into() }]);
347
348 let fee1 = amount_to_send.saturating_div(2);
351 let fee2 = amount_to_send.saturating_sub(fee1);
352 let fees_half_1 = Asset::from((fees.id.clone(), Fungible(fee1)));
353 let fees_half_2 = Asset::from((fees.id.clone(), Fungible(fee2)));
354
355 let reserve_context = <XcmConfig as xcm_executor::Config>::UniversalLocation::get();
356 let reserve_fees = fees_half_1.reanchored(&reserve, &reserve_context).unwrap();
358 let dest_fees = fees_half_2.reanchored(&dest, &reserve_context).unwrap();
360 let assets_reanchored = assets.reanchored(&reserve, &reserve_context).unwrap();
362 let dest = dest.reanchored(&reserve, &reserve_context).unwrap();
364 let xcm_on_dest = Xcm(vec![
366 BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() },
367 DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary },
368 ]);
369 let msg = Xcm(vec![
371 WithdrawAsset(assets_reanchored),
372 ClearOrigin,
373 BuyExecution { fees: reserve_fees, weight_limit },
374 DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
375 ]);
376
377 let mut block_builder = client.init_polkadot_block_builder();
378
379 let make_para_available =
381 construct_extrinsic(
382 &client,
383 polkadot_test_runtime::RuntimeCall::Sudo(pallet_sudo::Call::sudo {
384 call: Box::new(polkadot_test_runtime::RuntimeCall::System(
385 frame_system::Call::set_storage {
386 items: vec![(
387 polkadot_runtime_parachains::paras::Heads::<
388 polkadot_test_runtime::Runtime,
389 >::hashed_key_for(2000u32),
390 vec![1, 2, 3],
391 )],
392 },
393 )),
394 }),
395 sp_keyring::Sr25519Keyring::Alice,
396 0,
397 );
398
399 let execute = construct_extrinsic(
401 &client,
402 polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute {
403 message: Box::new(VersionedXcm::from(msg)),
404 max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024),
405 }),
406 sp_keyring::Sr25519Keyring::Alice,
407 1,
408 );
409
410 block_builder
411 .push_polkadot_extrinsic(make_para_available)
412 .expect("pushes extrinsic");
413 block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic");
414
415 let block = block_builder.build().expect("Finalizes the block").block;
416 let block_hash = block.hash();
417
418 futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block))
419 .expect("imports the block");
420
421 client.state_at(block_hash).expect("state should exist").inspect_state(|| {
422 assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
423 r.event,
424 polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted {
425 outcome: Outcome::Complete { .. }
426 }),
427 )));
428 });
429}