// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . #![cfg_attr(not(feature = "std"), no_std)] #![cfg(test)] use codec::Encode; use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; use polkadot_test_client::{ BlockBuilderExt, ClientBlockImportExt, DefaultTestClientBuilderExt, InitPolkadotBlockBuilder, TestClientBuilder, TestClientBuilderExt, }; use polkadot_test_runtime::{pallet_test_notifier, xcm_config::XcmConfig}; use polkadot_test_service::construct_extrinsic; use sp_runtime::traits::Block; use sp_state_machine::InspectState; use xcm::{latest::prelude::*, VersionedResponse, VersionedXcm}; use xcm_executor::traits::WeightBounds; #[test] fn basic_buy_fees_message_executes() { sp_tracing::try_init_simple(); let mut client = TestClientBuilder::new().build(); let msg = Xcm(vec![ WithdrawAsset((Parent, 100).into()), BuyExecution { fees: (Parent, 1).into(), weight_limit: Unlimited }, DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Parent.into() }, ]); let mut block_builder = client.init_polkadot_block_builder(); let execute = construct_extrinsic( &client, polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute { message: Box::new(VersionedXcm::from(msg)), max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), }), sp_keyring::Sr25519Keyring::Alice, 0, ); block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); let block = block_builder.build().expect("Finalizes the block").block; let block_hash = block.hash(); futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) .expect("imports the block"); client.state_at(block_hash).expect("state should exist").inspect_state(|| { assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( r.event, polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(_) }), ))); }); } #[test] fn transact_recursion_limit_works() { sp_tracing::try_init_simple(); let mut client = TestClientBuilder::new().build(); let mut msg = Xcm(vec![ClearOrigin]); let max_weight = ::Weigher::weight(&mut msg).unwrap(); let mut call = polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute { message: Box::new(VersionedXcm::from(msg)), max_weight, }); for _ in 0..11 { let mut msg = Xcm(vec![ WithdrawAsset((Parent, 1_000).into()), BuyExecution { fees: (Parent, 1).into(), weight_limit: Unlimited }, Transact { origin_kind: OriginKind::Native, require_weight_at_most: call.get_dispatch_info().weight, call: call.encode().into(), }, ]); let max_weight = ::Weigher::weight(&mut msg).unwrap(); call = polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute { message: Box::new(VersionedXcm::from(msg)), max_weight, }); } let mut block_builder = client.init_polkadot_block_builder(); let execute = construct_extrinsic(&client, call, sp_keyring::Sr25519Keyring::Alice, 0); block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); let block = block_builder.build().expect("Finalizes the block").block; let block_hash = block.hash(); futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) .expect("imports the block"); client.state_at(block_hash).expect("state should exist").inspect_state(|| { assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( r.event, polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted { outcome: Outcome::Incomplete(_, XcmError::ExceedsStackLimit) }), ))); }); } #[test] fn query_response_fires() { use pallet_test_notifier::Event::*; use pallet_xcm::QueryStatus; use polkadot_test_runtime::RuntimeEvent::TestNotifier; sp_tracing::try_init_simple(); let mut client = TestClientBuilder::new().build(); let mut block_builder = client.init_polkadot_block_builder(); let execute = construct_extrinsic( &client, polkadot_test_runtime::RuntimeCall::TestNotifier( pallet_test_notifier::Call::prepare_new_query {}, ), sp_keyring::Sr25519Keyring::Alice, 0, ); block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); let block = block_builder.build().expect("Finalizes the block").block; let block_hash = block.hash(); futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) .expect("imports the block"); let mut query_id = None; client.state_at(block_hash).expect("state should exist").inspect_state(|| { for r in polkadot_test_runtime::System::events().iter() { match r.event { TestNotifier(QueryPrepared(q)) => query_id = Some(q), _ => (), } } }); let query_id = query_id.unwrap(); let mut block_builder = client.init_polkadot_block_builder(); let response = Response::ExecutionResult(None); let max_weight = Weight::from_parts(1_000_000, 1024 * 1024); let querier = Some(Here.into()); let msg = Xcm(vec![QueryResponse { query_id, response, max_weight, querier }]); let msg = Box::new(VersionedXcm::from(msg)); let execute = construct_extrinsic( &client, polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute { message: msg, max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), }), sp_keyring::Sr25519Keyring::Alice, 1, ); block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); let block = block_builder.build().expect("Finalizes the block").block; let block_hash = block.hash(); futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) .expect("imports the block"); client.state_at(block_hash).expect("state should exist").inspect_state(|| { assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( r.event, polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::ResponseReady { query_id: q, response: Response::ExecutionResult(None), }) if q == query_id, ))); assert_eq!( polkadot_test_runtime::Xcm::query(query_id), Some(QueryStatus::Ready { response: VersionedResponse::V3(Response::ExecutionResult(None)), at: 2u32.into() }), ) }); } #[test] fn query_response_elicits_handler() { use pallet_test_notifier::Event::*; use polkadot_test_runtime::RuntimeEvent::TestNotifier; sp_tracing::try_init_simple(); let mut client = TestClientBuilder::new().build(); let mut block_builder = client.init_polkadot_block_builder(); let execute = construct_extrinsic( &client, polkadot_test_runtime::RuntimeCall::TestNotifier( pallet_test_notifier::Call::prepare_new_notify_query {}, ), sp_keyring::Sr25519Keyring::Alice, 0, ); block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); let block = block_builder.build().expect("Finalizes the block").block; let block_hash = block.hash(); futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) .expect("imports the block"); let mut query_id = None; client.state_at(block_hash).expect("state should exist").inspect_state(|| { for r in polkadot_test_runtime::System::events().iter() { match r.event { TestNotifier(NotifyQueryPrepared(q)) => query_id = Some(q), _ => (), } } }); let query_id = query_id.unwrap(); let mut block_builder = client.init_polkadot_block_builder(); let response = Response::ExecutionResult(None); let max_weight = Weight::from_parts(1_000_000, 1024 * 1024); let querier = Some(Here.into()); let msg = Xcm(vec![QueryResponse { query_id, response, max_weight, querier }]); let execute = construct_extrinsic( &client, polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute { message: Box::new(VersionedXcm::from(msg)), max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), }), sp_keyring::Sr25519Keyring::Alice, 1, ); block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); let block = block_builder.build().expect("Finalizes the block").block; let block_hash = block.hash(); futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) .expect("imports the block"); client.state_at(block_hash).expect("state should exist").inspect_state(|| { assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( r.event, TestNotifier(ResponseReceived( MultiLocation { parents: 0, interior: X1(Junction::AccountId32 { .. }) }, q, Response::ExecutionResult(None), )) if q == query_id, ))); }); }