pallet_session_validator_management/migrations/
authority_keys.rs

1//! Implements a re-usable migration for the authority keys type
2//!
3//! # Usage
4//!
5//! **Important**: This migration assumes that the runtime is using [pallet_session] and will
6//! migrate that pallet's key storage as well.
7//!
8//! Authority keys migration is done by adding [AuthorityKeysMigration] to the runtime
9//! migrations as part of the runtime upgrade that will change the key type.
10//!
11//! Preserve the old authority keys type and implement [UpgradeAuthorityKeys] trait for it.
12//! For example, if a chain that originally used Aura and Grandpa keys is being upgraded to
13//! also use Beefy, the definition of the legacy keys type could look like this:
14//!
15//! ```rust
16//! use pallet_session_validator_management::migrations::authority_keys::UpgradeAuthorityKeys;
17//! use sp_core::*;
18//! use sp_runtime::impl_opaque_keys;
19//!
20//! # use sp_runtime::BoundToRuntimeAppPublic;
21//!
22//! # pub struct Aura;
23//! # impl BoundToRuntimeAppPublic for Aura { type Public = sp_runtime::app_crypto::sr25519::AppPublic; }
24//! # pub struct Grandpa;
25//! # impl BoundToRuntimeAppPublic for Grandpa { type Public = sp_runtime::app_crypto::ed25519::AppPublic; }
26//! # pub struct Beefy;
27//! # impl BoundToRuntimeAppPublic for Beefy { type Public = sp_runtime::app_crypto::ecdsa::AppPublic; }
28//!
29//! impl_opaque_keys! {
30//! 	pub struct AuthorityKeys {
31//! 		pub aura: Aura,
32//! 		pub grandpa: Grandpa,
33//! 		pub beefy: Beefy,
34//! 	}
35//! }
36//!
37//! impl_opaque_keys! {
38//! 	pub struct LegacyAuthorityKeys {
39//! 		pub aura: Aura,
40//! 		pub grandpa: Grandpa,
41//! 	}
42//! }
43//!
44//! impl UpgradeAuthorityKeys<AuthorityKeys> for LegacyAuthorityKeys {
45//! 	fn upgrade(self) -> AuthorityKeys {
46//! 		AuthorityKeys {
47//! 			aura: self.aura,
48//! 			grandpa: self.grandpa,
49//! 			beefy: ecdsa::Public::default().into(),
50//! 		}
51//! 	}
52//! }
53//! ```
54//!
55//! The `upgrade` implementation can arbitrarily transform the data but must not fail for any
56//! of the migrated key sets.
57//!
58//! After implementing [UpgradeAuthorityKeys], the migration can be added to the runtime's
59//! migration set:
60//! ```rust,ignore
61//! pub type Migrations = (
62//! 	AuthorityKeysMigration<Runtime, LegacySessionKeys, 0, 1>,
63//! 	// ...other migrations
64//! );
65//!
66//! // ...
67//!
68//! pub type Executive = Executive<
69//! 	Runtime,
70//! 	Block,
71//! 	ChainContext<Runtime>,
72//! 	Runtime,
73//! 	AllPalletsWithSystem,
74//! 	Migrations,
75//! >;
76//! ```
77//!
78//! Note that [AuthorityKeysMigration] is parametrized by the session keys versions from which
79//! and to which it migrates, to guarantee idempotency. Current session keys version can be
80//! obtained by reading the [AuthorityKeysVersion] storage and by default starts as 0.
81
82extern crate alloc;
83
84use crate::*;
85use alloc::vec::Vec;
86use core::marker::PhantomData;
87use frame_support::traits::OnRuntimeUpgrade;
88use sp_core::Get;
89use sp_runtime::BoundedVec;
90use sp_runtime::traits::{Member, OpaqueKeys};
91
92/// Infallible cast from old to current `T::AuthorityKeys`, used for storage migration
93pub trait UpgradeAuthorityKeys<NewAuthorityKeys> {
94	/// Should cast the old session keys type to the new one
95	fn upgrade(self) -> NewAuthorityKeys;
96}
97
98/// Migrates existing committee members data in storage to use new type `AuthorityKeys`
99///
100/// This migration is versioned and will only be applied when on-chain session keys version
101/// as read from [AuthorityKeysVersion] storage is equal to `FROM_VERSION` and will
102/// set the version to `TO_VERSION`.
103///
104/// **Important**: This migration assumes that the runtime is using [pallet_session] and will
105/// migrate that pallet's key storage as well.
106pub struct AuthorityKeysMigration<
107	T,
108	OldAuthorityKeys,
109	const FROM_VERSION: u32,
110	const TO_VERSION: u32,
111> where
112	T: crate::Config,
113	OldAuthorityKeys: UpgradeAuthorityKeys<T::AuthorityKeys> + Member + Decode + Clone,
114{
115	_phantom: PhantomData<(T, OldAuthorityKeys)>,
116}
117
118impl<T: crate::Config, OldAuthorityKeys, const FROM_VERSION: u32, const TO_VERSION: u32>
119	AuthorityKeysMigration<T, OldAuthorityKeys, FROM_VERSION, TO_VERSION>
120where
121	OldAuthorityKeys: UpgradeAuthorityKeys<T::AuthorityKeys> + Member + Decode + Clone,
122{
123	/// Casts a [BoundedVec] of old committee member values to the new ones
124	fn upgrade_bounded_vec(
125		old: BoundedVec<CommitteeMember<T::AuthorityId, OldAuthorityKeys>, T::MaxValidators>,
126	) -> BoundedVec<CommitteeMemberOf<T>, T::MaxValidators> {
127		BoundedVec::truncate_from(
128			old.into_iter()
129				.map(|old| old.map_authority_keys(OldAuthorityKeys::upgrade))
130				.collect::<Vec<_>>(),
131		)
132	}
133
134	/// Casts old committee member values in a [CommitteeInfo] into new ones
135	fn upgrade_committee_info(
136		old: CommitteeInfo<T::AuthorityId, OldAuthorityKeys, T::MaxValidators>,
137	) -> CommitteeInfoOf<T> {
138		CommitteeInfo { epoch: old.epoch, committee: Self::upgrade_bounded_vec(old.committee) }
139	}
140}
141
142impl<T, OldAuthorityKeys, const FROM_VERSION: u32, const TO_VERSION: u32> OnRuntimeUpgrade
143	for AuthorityKeysMigration<T, OldAuthorityKeys, FROM_VERSION, TO_VERSION>
144where
145	T: crate::Config + pallet_session::Config<Keys = <T as crate::Config>::AuthorityKeys>,
146	OldAuthorityKeys: UpgradeAuthorityKeys<T::AuthorityKeys> + OpaqueKeys + Member + Decode + Clone,
147{
148	fn on_runtime_upgrade() -> sp_runtime::Weight {
149		let current_version = crate::AuthorityKeysVersion::<T>::get();
150
151		let mut weight = T::DbWeight::get().reads_writes(1, 0);
152
153		if TO_VERSION <= current_version {
154			log::info!(
155				"🚚 AuthorityKeysMigration {FROM_VERSION}->{TO_VERSION} can be removed; authority keys storage is already at version {current_version}."
156			);
157			return weight;
158		}
159		if current_version != FROM_VERSION {
160			log::warn!(
161				"🚚 AuthorityKeysMigration {FROM_VERSION}->{TO_VERSION} can not be applied to authority keys storage at version {current_version}."
162			);
163			return weight;
164		}
165
166		if let Some(new) = CurrentCommittee::<T>::translate::<
167			CommitteeInfo<T::AuthorityId, OldAuthorityKeys, T::MaxValidators>,
168			_,
169		>(|old| old.map(Self::upgrade_committee_info))
170		.expect("Decoding of the old value must succeed")
171		{
172			CurrentCommittee::<T>::put(new);
173			log::info!("🚚️ Migrated current committee storage to version {TO_VERSION}");
174			weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
175		}
176
177		if let Some(new) = NextCommittee::<T>::translate::<
178			CommitteeInfo<T::AuthorityId, OldAuthorityKeys, T::MaxValidators>,
179			_,
180		>(|old| old.map(Self::upgrade_committee_info))
181		.expect("Decoding of the old value must succeed")
182		{
183			NextCommittee::<T>::put(new);
184			log::info!("🚚️ Migrated new committee storage to version {TO_VERSION}");
185			weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
186		}
187
188		pallet_session::Pallet::<T>::upgrade_keys(|_id, old_keys| {
189			OldAuthorityKeys::upgrade(old_keys)
190		});
191		weight.saturating_add(T::DbWeight::get().reads_writes(2, 2));
192		log::info!("🚚️ Migrated keys in pallet_session to version {TO_VERSION}");
193
194		crate::AuthorityKeysVersion::<T>::set(TO_VERSION);
195		weight.saturating_add(T::DbWeight::get().reads_writes(0, 1));
196
197		weight
198	}
199}