referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_common/
identity_migrator.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! This pallet is designed to go into a source chain and destination chain to migrate data. The
17//! design motivations are:
18//!
19//! - Call some function on the source chain that executes some migration (clearing state,
20//!   forwarding an XCM program).
21//! - Call some function (probably from an XCM program) on the destination chain.
22//! - Avoid cluttering the source pallet with new dispatchables that are unrelated to its
23//!   functionality and only used for migration.
24//!
25//! After the migration is complete, the pallet may be removed from both chains' runtimes as well as
26//! the `polkadot-runtime-common` crate.
27
28use frame_support::{dispatch::DispatchResult, traits::Currency, weights::Weight};
29pub use pallet::*;
30use pallet_identity;
31use sp_core::Get;
32
33#[cfg(feature = "runtime-benchmarks")]
34use frame_benchmarking::{account, v2::*, BenchmarkError};
35
36pub trait WeightInfo {
37	fn reap_identity(r: u32, s: u32) -> Weight;
38	fn poke_deposit() -> Weight;
39}
40
41impl WeightInfo for () {
42	fn reap_identity(_r: u32, _s: u32) -> Weight {
43		Weight::MAX
44	}
45	fn poke_deposit() -> Weight {
46		Weight::MAX
47	}
48}
49
50pub struct TestWeightInfo;
51impl WeightInfo for TestWeightInfo {
52	fn reap_identity(_r: u32, _s: u32) -> Weight {
53		Weight::zero()
54	}
55	fn poke_deposit() -> Weight {
56		Weight::zero()
57	}
58}
59
60// Must use the same `Balance` as `T`'s Identity pallet to handle deposits.
61type BalanceOf<T> = <<T as pallet_identity::Config>::Currency as Currency<
62	<T as frame_system::Config>::AccountId,
63>>::Balance;
64
65#[frame_support::pallet]
66pub mod pallet {
67	use super::*;
68	use frame_support::{
69		dispatch::{DispatchResultWithPostInfo, PostDispatchInfo},
70		pallet_prelude::*,
71		traits::EnsureOrigin,
72	};
73	use frame_system::pallet_prelude::*;
74
75	#[pallet::pallet]
76	pub struct Pallet<T>(_);
77
78	#[pallet::config]
79	pub trait Config: frame_system::Config + pallet_identity::Config {
80		/// Overarching event type.
81		#[allow(deprecated)]
82		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
83
84		/// The origin that can reap identities. Expected to be `EnsureSigned<AccountId>` on the
85		/// source chain such that anyone can all this function.
86		type Reaper: EnsureOrigin<Self::RuntimeOrigin>;
87
88		/// A handler for what to do when an identity is reaped.
89		type ReapIdentityHandler: OnReapIdentity<Self::AccountId>;
90
91		/// Weight information for the extrinsics in the pallet.
92		type WeightInfo: WeightInfo;
93	}
94
95	#[pallet::event]
96	#[pallet::generate_deposit(pub(super) fn deposit_event)]
97	pub enum Event<T: Config> {
98		/// The identity and all sub accounts were reaped for `who`.
99		IdentityReaped { who: T::AccountId },
100		/// The deposits held for `who` were updated. `identity` is the new deposit held for
101		/// identity info, and `subs` is the new deposit held for the sub-accounts.
102		DepositUpdated { who: T::AccountId, identity: BalanceOf<T>, subs: BalanceOf<T> },
103	}
104
105	#[pallet::call]
106	impl<T: Config> Pallet<T> {
107		/// Reap the `IdentityInfo` of `who` from the Identity pallet of `T`, unreserving any
108		/// deposits held and removing storage items associated with `who`.
109		#[pallet::call_index(0)]
110		#[pallet::weight(<T as pallet::Config>::WeightInfo::reap_identity(
111				T::MaxRegistrars::get(),
112				T::MaxSubAccounts::get()
113		))]
114		pub fn reap_identity(
115			origin: OriginFor<T>,
116			who: T::AccountId,
117		) -> DispatchResultWithPostInfo {
118			T::Reaper::ensure_origin(origin)?;
119			// - number of registrars (required to calculate weight)
120			// - byte size of `IdentityInfo` (required to calculate remote deposit)
121			// - number of sub accounts (required to calculate both weight and remote deposit)
122			let (registrars, bytes, subs) = pallet_identity::Pallet::<T>::reap_identity(&who)?;
123			T::ReapIdentityHandler::on_reap_identity(&who, bytes, subs)?;
124			Self::deposit_event(Event::IdentityReaped { who });
125			let post = PostDispatchInfo {
126				actual_weight: Some(<T as pallet::Config>::WeightInfo::reap_identity(
127					registrars, subs,
128				)),
129				pays_fee: Pays::No,
130			};
131			Ok(post)
132		}
133
134		/// Update the deposit of `who`. Meant to be called by the system with an XCM `Transact`
135		/// Instruction.
136		#[pallet::call_index(1)]
137		#[pallet::weight(<T as pallet::Config>::WeightInfo::poke_deposit())]
138		pub fn poke_deposit(origin: OriginFor<T>, who: T::AccountId) -> DispatchResultWithPostInfo {
139			ensure_root(origin)?;
140			let (id_deposit, subs_deposit) = pallet_identity::Pallet::<T>::poke_deposit(&who)?;
141			Self::deposit_event(Event::DepositUpdated {
142				who,
143				identity: id_deposit,
144				subs: subs_deposit,
145			});
146			Ok(Pays::No.into())
147		}
148	}
149}
150
151/// Trait to handle reaping identity from state.
152pub trait OnReapIdentity<AccountId> {
153	/// What to do when an identity is reaped. For example, the implementation could send an XCM
154	/// program to another chain. Concretely, a type implementing this trait in the Polkadot
155	/// runtime would teleport enough DOT to the People Chain to cover the Identity deposit there.
156	///
157	/// This could also directly include `Transact { poke_deposit(..), ..}`.
158	///
159	/// Inputs
160	/// - `who`: Whose identity was reaped.
161	/// - `bytes`: The byte size of `IdentityInfo`.
162	/// - `subs`: The number of sub-accounts they had.
163	fn on_reap_identity(who: &AccountId, bytes: u32, subs: u32) -> DispatchResult;
164
165	/// Ensure that identity reaping will be succesful in benchmarking.
166	///
167	/// Should setup the state in a way that the same call ot `[Self::on_reap_identity]` will be
168	/// successful.
169	#[cfg(feature = "runtime-benchmarks")]
170	fn ensure_successful_identity_reaping(who: &AccountId, bytes: u32, subs: u32);
171}
172
173impl<AccountId> OnReapIdentity<AccountId> for () {
174	fn on_reap_identity(_who: &AccountId, _bytes: u32, _subs: u32) -> DispatchResult {
175		Ok(())
176	}
177
178	#[cfg(feature = "runtime-benchmarks")]
179	fn ensure_successful_identity_reaping(_: &AccountId, _: u32, _: u32) {}
180}
181
182#[cfg(feature = "runtime-benchmarks")]
183#[benchmarks]
184mod benchmarks {
185	use super::*;
186	use alloc::{boxed::Box, vec, vec::Vec};
187	use codec::Encode;
188	use frame_support::traits::EnsureOrigin;
189	use frame_system::RawOrigin;
190	use pallet_identity::{Data, IdentityInformationProvider, Judgement, Pallet as Identity};
191	use sp_runtime::{
192		traits::{Bounded, Hash, StaticLookup},
193		Saturating,
194	};
195
196	const SEED: u32 = 0;
197
198	fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
199		let events = frame_system::Pallet::<T>::events();
200		let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
201		let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
202		assert_eq!(event, &system_event);
203	}
204
205	#[benchmark]
206	fn reap_identity(
207		r: Linear<0, { T::MaxRegistrars::get() }>,
208		s: Linear<0, { T::MaxSubAccounts::get() }>,
209	) -> Result<(), BenchmarkError> {
210		// set up target
211		let target: T::AccountId = account("target", 0, SEED);
212		let target_origin =
213			<T as frame_system::Config>::RuntimeOrigin::from(RawOrigin::Signed(target.clone()));
214		let target_lookup = T::Lookup::unlookup(target.clone());
215		let _ = T::Currency::make_free_balance_be(&target, BalanceOf::<T>::max_value());
216
217		// set identity
218		let info = <T as pallet_identity::Config>::IdentityInformation::create_identity_info();
219		Identity::<T>::set_identity(
220			RawOrigin::Signed(target.clone()).into(),
221			Box::new(info.clone()),
222		)?;
223
224		// create and set subs
225		let mut subs = Vec::new();
226		let data = Data::Raw(vec![0; 32].try_into().unwrap());
227		for ii in 0..s {
228			let sub_account = account("sub", ii, SEED);
229			subs.push((sub_account, data.clone()));
230		}
231		Identity::<T>::set_subs(target_origin.clone(), subs.clone())?;
232
233		T::ReapIdentityHandler::ensure_successful_identity_reaping(
234			&target,
235			info.encoded_size() as u32,
236			subs.len() as u32,
237		);
238
239		// add registrars and provide judgements
240		let registrar_origin = T::RegistrarOrigin::try_successful_origin()
241			.expect("RegistrarOrigin has no successful origin required for the benchmark");
242		for ii in 0..r {
243			// registrar account
244			let registrar: T::AccountId = account("registrar", ii, SEED);
245			let registrar_lookup = T::Lookup::unlookup(registrar.clone());
246			let _ = <T as pallet_identity::Config>::Currency::make_free_balance_be(
247				&registrar,
248				<T as pallet_identity::Config>::Currency::minimum_balance(),
249			);
250
251			// add registrar
252			Identity::<T>::add_registrar(registrar_origin.clone(), registrar_lookup)?;
253			Identity::<T>::set_fee(RawOrigin::Signed(registrar.clone()).into(), ii, 10u32.into())?;
254			let fields = <T as pallet_identity::Config>::IdentityInformation::all_fields();
255			Identity::<T>::set_fields(RawOrigin::Signed(registrar.clone()).into(), ii, fields)?;
256
257			// request and provide judgement
258			Identity::<T>::request_judgement(target_origin.clone(), ii, 10u32.into())?;
259			Identity::<T>::provide_judgement(
260				RawOrigin::Signed(registrar).into(),
261				ii,
262				target_lookup.clone(),
263				Judgement::Reasonable,
264				<T as frame_system::Config>::Hashing::hash_of(&info),
265			)?;
266		}
267
268		let origin = T::Reaper::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
269
270		#[extrinsic_call]
271		_(origin as T::RuntimeOrigin, target.clone());
272
273		assert_last_event::<T>(Event::<T>::IdentityReaped { who: target.clone() }.into());
274
275		let fields = <T as pallet_identity::Config>::IdentityInformation::all_fields();
276		assert!(!Identity::<T>::has_identity(&target, fields));
277		assert_eq!(Identity::<T>::subs(&target).len(), 0);
278
279		Ok(())
280	}
281
282	#[benchmark]
283	fn poke_deposit() -> Result<(), BenchmarkError> {
284		let target: T::AccountId = account("target", 0, SEED);
285		let _ = T::Currency::make_free_balance_be(&target, BalanceOf::<T>::max_value());
286		let info = <T as pallet_identity::Config>::IdentityInformation::create_identity_info();
287
288		let _ = Identity::<T>::set_identity_no_deposit(&target, info.clone());
289
290		let sub_account: T::AccountId = account("sub", 0, SEED);
291		let name = Data::Raw(b"benchsub".to_vec().try_into().unwrap());
292		let _ = Identity::<T>::set_subs_no_deposit(&target, vec![(sub_account.clone(), name)]);
293
294		// expected deposits
295		let expected_id_deposit = <T as pallet_identity::Config>::BasicDeposit::get()
296			.saturating_add(
297				<T as pallet_identity::Config>::ByteDeposit::get()
298					.saturating_mul(<BalanceOf<T>>::from(info.encoded_size() as u32)),
299			);
300		// only 1 sub
301		let expected_sub_deposit = <T as pallet_identity::Config>::SubAccountDeposit::get();
302
303		#[extrinsic_call]
304		_(RawOrigin::Root, target.clone());
305
306		assert_last_event::<T>(
307			Event::<T>::DepositUpdated {
308				who: target,
309				identity: expected_id_deposit,
310				subs: expected_sub_deposit,
311			}
312			.into(),
313		);
314
315		Ok(())
316	}
317
318	impl_benchmark_test_suite!(
319		Pallet,
320		crate::integration_tests::new_test_ext(),
321		crate::integration_tests::Test,
322	);
323}