referrerpolicy=no-referrer-when-downgrade

parachains_runtimes_test_utils/
test_cases.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Module contains predefined test-case scenarios for `Runtime` with common functionality.
18
19use crate::{
20	AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, ExtBuilder, GovernanceOrigin,
21	RuntimeCallOf, RuntimeOriginOf, ValidatorIdOf,
22};
23use codec::Encode;
24use frame_support::{
25	assert_ok,
26	traits::{Get, OriginTrait},
27	weights::WeightToFee as WeightToFeeT,
28};
29use parachains_common::AccountId;
30use sp_runtime::{
31	traits::{Block as BlockT, SaturatedConversion, StaticLookup},
32	DispatchError, Either,
33};
34use xcm::prelude::InstructionError;
35use xcm_runtime_apis::fees::{
36	runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, Error as XcmPaymentApiError,
37};
38
39type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
40	crate::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
41
42/// Test-case makes sure that `Runtime` can change storage constant via governance-like call
43pub fn change_storage_constant_by_governance_works<Runtime, StorageConstant, StorageConstantType>(
44	collator_session_key: CollatorSessionKeys<Runtime>,
45	runtime_para_id: u32,
46	governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
47	storage_constant_key_value: fn() -> (Vec<u8>, StorageConstantType),
48	new_storage_constant_value: fn(&StorageConstantType) -> StorageConstantType,
49) where
50	Runtime: frame_system::Config
51		+ pallet_balances::Config
52		+ pallet_session::Config
53		+ pallet_xcm::Config
54		+ parachain_info::Config
55		+ pallet_collator_selection::Config
56		+ cumulus_pallet_parachain_system::Config
57		+ pallet_timestamp::Config,
58	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
59	StorageConstant: Get<StorageConstantType>,
60	StorageConstantType: Encode + PartialEq + std::fmt::Debug,
61{
62	ExtBuilder::<Runtime>::default()
63		.with_collators(collator_session_key.collators())
64		.with_session_keys(collator_session_key.session_keys())
65		.with_para_id(runtime_para_id.into())
66		.with_tracing()
67		.build()
68		.execute_with(|| {
69			let (storage_constant_key, storage_constant_init_value): (
70				Vec<u8>,
71				StorageConstantType,
72			) = storage_constant_key_value();
73
74			// check delivery reward constant before (not stored yet, just as default value is used)
75			assert_eq!(StorageConstant::get(), storage_constant_init_value);
76			assert_eq!(sp_io::storage::get(&storage_constant_key), None);
77
78			let new_storage_constant_value =
79				new_storage_constant_value(&storage_constant_init_value);
80			assert_ne!(new_storage_constant_value, storage_constant_init_value);
81
82			// encode `set_storage` call
83			let set_storage_call =
84				RuntimeCallOf::<Runtime>::from(frame_system::Call::<Runtime>::set_storage {
85					items: vec![(
86						storage_constant_key.clone(),
87						new_storage_constant_value.encode(),
88					)],
89				});
90
91			// execute XCM with Transact to `set_storage` as governance does
92			assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
93				set_storage_call,
94				governance_origin
95			));
96
97			// check delivery reward constant after (stored)
98			assert_eq!(StorageConstant::get(), new_storage_constant_value);
99			assert_eq!(
100				sp_io::storage::get(&storage_constant_key),
101				Some(new_storage_constant_value.encode().into())
102			);
103		})
104}
105
106/// Test-case makes sure that `Runtime` can change storage constant via governance-like call
107pub fn set_storage_keys_by_governance_works<Runtime>(
108	collator_session_key: CollatorSessionKeys<Runtime>,
109	runtime_para_id: u32,
110	governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
111	storage_items: Vec<(Vec<u8>, Vec<u8>)>,
112	initialize_storage: impl FnOnce() -> (),
113	assert_storage: impl FnOnce() -> (),
114) where
115	Runtime: frame_system::Config
116		+ pallet_balances::Config
117		+ pallet_session::Config
118		+ pallet_xcm::Config
119		+ parachain_info::Config
120		+ pallet_collator_selection::Config
121		+ cumulus_pallet_parachain_system::Config
122		+ pallet_timestamp::Config,
123	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
124{
125	let mut runtime = ExtBuilder::<Runtime>::default()
126		.with_collators(collator_session_key.collators())
127		.with_session_keys(collator_session_key.session_keys())
128		.with_para_id(runtime_para_id.into())
129		.with_tracing()
130		.build();
131	runtime.execute_with(|| {
132		initialize_storage();
133	});
134	runtime.execute_with(|| {
135		// encode `kill_storage` call
136		let kill_storage_call =
137			RuntimeCallOf::<Runtime>::from(frame_system::Call::<Runtime>::set_storage {
138				items: storage_items.clone(),
139			});
140
141		// execute XCM with Transact to `set_storage` as governance does
142		assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
143			kill_storage_call,
144			governance_origin
145		));
146	});
147	runtime.execute_with(|| {
148		assert_storage();
149	});
150}
151
152pub fn xcm_payment_api_with_native_token_works<
153	Runtime,
154	RuntimeCall,
155	RuntimeOrigin,
156	Block,
157	WeightToFee,
158>()
159where
160	Runtime: XcmPaymentApiV1<Block>
161		+ frame_system::Config<RuntimeOrigin = RuntimeOrigin, AccountId = AccountId>
162		+ pallet_balances::Config<Balance = u128>
163		+ pallet_session::Config
164		+ pallet_xcm::Config
165		+ parachain_info::Config
166		+ pallet_collator_selection::Config
167		+ cumulus_pallet_parachain_system::Config
168		+ cumulus_pallet_xcmp_queue::Config
169		+ pallet_timestamp::Config,
170	ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
171	RuntimeOrigin: OriginTrait<AccountId = <Runtime as frame_system::Config>::AccountId>,
172	<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
173		From<<Runtime as frame_system::Config>::AccountId>,
174	Block: BlockT,
175	WeightToFee: WeightToFeeT,
176{
177	use xcm::prelude::*;
178	ExtBuilder::<Runtime>::default().build().execute_with(|| {
179		let transfer_amount = 100u128;
180		let xcm_to_weigh = Xcm::<RuntimeCall>::builder_unsafe()
181			.withdraw_asset((Here, transfer_amount))
182			.buy_execution((Here, transfer_amount), Unlimited)
183			.deposit_asset(AllCounted(1), [1u8; 32])
184			.build();
185		let versioned_xcm_to_weigh = VersionedXcm::from(xcm_to_weigh.clone().into());
186
187		// We first try calling it with a lower XCM version.
188		let lower_version_xcm_to_weigh =
189			versioned_xcm_to_weigh.clone().into_version(XCM_VERSION - 1).unwrap();
190		let xcm_weight = Runtime::query_xcm_weight(lower_version_xcm_to_weigh)
191			.expect("xcm weight must be computed");
192
193		let expected_weight_fee: u128 = WeightToFee::weight_to_fee(&xcm_weight).saturated_into();
194
195		let native_token: Location = Parent.into();
196		let native_token_versioned = VersionedAssetId::from(AssetId(native_token));
197		let lower_version_native_token =
198			native_token_versioned.clone().into_version(XCM_VERSION - 1).unwrap();
199		let execution_fees =
200			Runtime::query_weight_to_asset_fee(xcm_weight, lower_version_native_token)
201				.expect("weight must be converted to native fee");
202
203		assert_eq!(execution_fees, expected_weight_fee);
204
205		// Now we call it with the latest version.
206		let xcm_weight =
207			Runtime::query_xcm_weight(versioned_xcm_to_weigh).expect("xcm weight must be computed");
208
209		let expected_weight_fee: u128 = WeightToFee::weight_to_fee(&xcm_weight).saturated_into();
210
211		let execution_fees = Runtime::query_weight_to_asset_fee(xcm_weight, native_token_versioned)
212			.expect("weight must be converted to native fee");
213
214		assert_eq!(execution_fees, expected_weight_fee);
215
216		// If we call it with anything other than the native token it will error.
217		let non_existent_token: Location = Here.into();
218		let non_existent_token_versioned = VersionedAssetId::from(AssetId(non_existent_token));
219		let execution_fees =
220			Runtime::query_weight_to_asset_fee(xcm_weight, non_existent_token_versioned);
221		assert_eq!(execution_fees, Err(XcmPaymentApiError::AssetNotFound));
222	});
223}
224
225/// Generic test case for Cumulus-based parachain that verifies if runtime can process
226/// `frame_system::Call::authorize_upgrade` from governance system.
227pub fn can_governance_authorize_upgrade<Runtime, RuntimeOrigin>(
228	governance_origin: GovernanceOrigin<RuntimeOrigin>,
229) -> Result<(), Either<DispatchError, InstructionError>>
230where
231	Runtime: BasicParachainRuntime
232		+ frame_system::Config<RuntimeOrigin = RuntimeOrigin, AccountId = AccountId>,
233{
234	ExtBuilder::<Runtime>::default().build().execute_with(|| {
235		// check before
236		assert!(frame_system::Pallet::<Runtime>::authorized_upgrade().is_none());
237
238		// execute call as governance does
239		let code_hash = Runtime::Hash::default();
240		let authorize_upgrade_call: <Runtime as frame_system::Config>::RuntimeCall =
241			frame_system::Call::<Runtime>::authorize_upgrade { code_hash }.into();
242		RuntimeHelper::<Runtime>::execute_as_governance_call(
243			authorize_upgrade_call,
244			governance_origin,
245		)?;
246
247		// check after
248		match frame_system::Pallet::<Runtime>::authorized_upgrade() {
249			None => Err(Either::Left(frame_system::Error::<Runtime>::NothingAuthorized.into())),
250			Some(_) => Ok(()),
251		}
252	})
253}