referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/pallet/expand/
hooks.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use crate::pallet::Def;
19
20/// * implement the individual traits using the Hooks trait
21pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
22	let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() {
23		Some(hooks) => {
24			let where_clause = hooks.where_clause.clone();
25			let span = hooks.attr_span;
26			let has_runtime_upgrade = hooks.has_runtime_upgrade;
27			(where_clause, span, has_runtime_upgrade)
28		},
29		None => (def.config.where_clause.clone(), def.pallet_struct.attr_span, false),
30	};
31
32	let frame_support = &def.frame_support;
33	let type_impl_gen = &def.type_impl_generics(span);
34	let type_use_gen = &def.type_use_generics(span);
35	let pallet_ident = &def.pallet_struct.pallet;
36	let frame_system = &def.frame_system;
37	let pallet_name = quote::quote! {
38		<
39			<T as #frame_system::Config>::PalletInfo
40			as
41			#frame_support::traits::PalletInfo
42		>::name::<Self>().unwrap_or("<unknown pallet name>")
43	};
44
45	let initialize_on_chain_storage_version = if let Some(in_code_version) =
46		&def.pallet_struct.storage_version
47	{
48		quote::quote! {
49			#frame_support::__private::log::info!(
50				target: #frame_support::LOG_TARGET,
51				"🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}",
52				#pallet_name,
53				#in_code_version
54			);
55			#in_code_version.put::<Self>();
56		}
57	} else {
58		quote::quote! {
59			let default_version = #frame_support::traits::StorageVersion::new(0);
60			#frame_support::__private::log::info!(
61				target: #frame_support::LOG_TARGET,
62				"🐥 New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.",
63				#pallet_name,
64				default_version
65			);
66			default_version.put::<Self>();
67		}
68	};
69
70	let log_runtime_upgrade = if has_runtime_upgrade {
71		// a migration is defined here.
72		quote::quote! {
73			#frame_support::__private::log::info!(
74				target: #frame_support::LOG_TARGET,
75				"⚠️ {} declares internal migrations (which *might* execute). \
76				 On-chain `{:?}` vs in-code storage version `{:?}`",
77				#pallet_name,
78				<Self as #frame_support::traits::GetStorageVersion>::on_chain_storage_version(),
79				<Self as #frame_support::traits::GetStorageVersion>::in_code_storage_version(),
80			);
81		}
82	} else {
83		// default.
84		quote::quote! {
85			#frame_support::__private::log::debug!(
86				target: #frame_support::LOG_TARGET,
87				"✅ no migration for {}",
88				#pallet_name,
89			);
90		}
91	};
92
93	let hooks_impl = if def.hooks.is_none() {
94		let frame_system = &def.frame_system;
95		quote::quote! {
96			impl<#type_impl_gen>
97				#frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::<T>>
98				for #pallet_ident<#type_use_gen> #where_clause {}
99		}
100	} else {
101		proc_macro2::TokenStream::new()
102	};
103
104	// If a storage version is set, we should ensure that the storage version on chain matches the
105	// in-code storage version. This assumes that `Executive` is running custom migrations before
106	// the pallets are called.
107	let post_storage_version_check = if def.pallet_struct.storage_version.is_some() {
108		quote::quote! {
109			let on_chain_version = <Self as #frame_support::traits::GetStorageVersion>::on_chain_storage_version();
110			let in_code_version = <Self as #frame_support::traits::GetStorageVersion>::in_code_storage_version();
111
112			if on_chain_version != in_code_version {
113				#frame_support::__private::log::error!(
114					target: #frame_support::LOG_TARGET,
115					"{}: On chain storage version {:?} doesn't match in-code storage version {:?}.",
116					#pallet_name,
117					on_chain_version,
118					in_code_version,
119				);
120
121				return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into());
122			}
123		}
124	} else {
125		quote::quote! {
126			let on_chain_version = <Self as #frame_support::traits::GetStorageVersion>::on_chain_storage_version();
127
128			if on_chain_version != #frame_support::traits::StorageVersion::new(0) {
129				#frame_support::__private::log::error!(
130					target: #frame_support::LOG_TARGET,
131					"{}: On chain storage version {:?} is set to non zero, \
132					 while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.",
133					#pallet_name,
134					on_chain_version,
135				);
136
137				return Err("On chain storage version set, while the pallet doesn't \
138							have the `#[pallet::storage_version(VERSION)]` attribute.".into());
139			}
140		}
141	};
142
143	quote::quote_spanned!(span =>
144		#hooks_impl
145
146		impl<#type_impl_gen>
147			#frame_support::traits::OnFinalize<#frame_system::pallet_prelude::BlockNumberFor::<T>>
148			for #pallet_ident<#type_use_gen> #where_clause
149		{
150			fn on_finalize(n: #frame_system::pallet_prelude::BlockNumberFor::<T>) {
151				#frame_support::__private::sp_tracing::enter_span!(
152					#frame_support::__private::sp_tracing::trace_span!("on_finalize")
153				);
154				<
155					Self as #frame_support::traits::Hooks<
156						#frame_system::pallet_prelude::BlockNumberFor::<T>
157					>
158				>::on_finalize(n)
159			}
160		}
161
162		impl<#type_impl_gen>
163			#frame_support::traits::OnIdle<#frame_system::pallet_prelude::BlockNumberFor::<T>>
164			for #pallet_ident<#type_use_gen> #where_clause
165		{
166			fn on_idle(
167				n: #frame_system::pallet_prelude::BlockNumberFor::<T>,
168				remaining_weight: #frame_support::weights::Weight
169			) -> #frame_support::weights::Weight {
170				<
171					Self as #frame_support::traits::Hooks<
172						#frame_system::pallet_prelude::BlockNumberFor::<T>
173					>
174				>::on_idle(n, remaining_weight)
175			}
176		}
177
178		impl<#type_impl_gen>
179			#frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::<T>>
180			for #pallet_ident<#type_use_gen> #where_clause
181		{
182			fn on_poll(
183				n: #frame_system::pallet_prelude::BlockNumberFor::<T>,
184				weight: &mut #frame_support::weights::WeightMeter
185			) {
186				<
187					Self as #frame_support::traits::Hooks<
188						#frame_system::pallet_prelude::BlockNumberFor::<T>
189					>
190				>::on_poll(n, weight);
191			}
192		}
193
194		impl<#type_impl_gen>
195			#frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::<T>>
196			for #pallet_ident<#type_use_gen> #where_clause
197		{
198			fn on_initialize(
199				n: #frame_system::pallet_prelude::BlockNumberFor::<T>
200			) -> #frame_support::weights::Weight {
201				#frame_support::__private::sp_tracing::enter_span!(
202					#frame_support::__private::sp_tracing::trace_span!("on_initialize")
203				);
204				<
205					Self as #frame_support::traits::Hooks<
206						#frame_system::pallet_prelude::BlockNumberFor::<T>
207					>
208				>::on_initialize(n)
209			}
210		}
211
212		impl<#type_impl_gen>
213			#frame_support::traits::BeforeAllRuntimeMigrations
214			for #pallet_ident<#type_use_gen> #where_clause
215		{
216			fn before_all_runtime_migrations() -> #frame_support::weights::Weight {
217				use #frame_support::traits::{Get, PalletInfoAccess};
218				use #frame_support::__private::hashing::twox_128;
219				use #frame_support::storage::unhashed::contains_prefixed_key;
220				#frame_support::__private::sp_tracing::enter_span!(
221					#frame_support::__private::sp_tracing::trace_span!("before_all")
222				);
223
224				// Check if the pallet has any keys set, including the storage version. If there are
225				// no keys set, the pallet was just added to the runtime and needs to have its
226				// version initialized.
227				let pallet_hashed_prefix = <Self as PalletInfoAccess>::name_hash();
228				let exists = contains_prefixed_key(&pallet_hashed_prefix);
229				if !exists {
230					#initialize_on_chain_storage_version
231					<T as #frame_system::Config>::DbWeight::get().reads_writes(1, 1)
232				} else {
233					<T as #frame_system::Config>::DbWeight::get().reads(1)
234				}
235			}
236		}
237
238		impl<#type_impl_gen>
239			#frame_support::traits::OnRuntimeUpgrade
240			for #pallet_ident<#type_use_gen> #where_clause
241		{
242			fn on_runtime_upgrade() -> #frame_support::weights::Weight {
243				#frame_support::__private::sp_tracing::enter_span!(
244					#frame_support::__private::sp_tracing::trace_span!("on_runtime_update")
245				);
246
247				// log info about the upgrade.
248				#log_runtime_upgrade
249
250				<
251					Self as #frame_support::traits::Hooks<
252						#frame_system::pallet_prelude::BlockNumberFor::<T>
253					>
254				>::on_runtime_upgrade()
255			}
256
257			#frame_support::try_runtime_enabled! {
258				fn pre_upgrade() -> Result<#frame_support::__private::Vec<u8>, #frame_support::sp_runtime::TryRuntimeError> {
259					<
260						Self
261						as
262						#frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::<T>>
263					>::pre_upgrade()
264				}
265
266				fn post_upgrade(state: #frame_support::__private::Vec<u8>) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> {
267					#post_storage_version_check
268
269					<
270						Self
271						as
272						#frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::<T>>
273					>::post_upgrade(state)
274				}
275			}
276		}
277
278		impl<#type_impl_gen>
279			#frame_support::traits::OffchainWorker<#frame_system::pallet_prelude::BlockNumberFor::<T>>
280			for #pallet_ident<#type_use_gen> #where_clause
281		{
282			fn offchain_worker(n: #frame_system::pallet_prelude::BlockNumberFor::<T>) {
283				<
284					Self as #frame_support::traits::Hooks<
285						#frame_system::pallet_prelude::BlockNumberFor::<T>
286					>
287				>::offchain_worker(n)
288			}
289		}
290
291		// Integrity tests are only required for when `std` is enabled.
292		#frame_support::std_enabled! {
293			impl<#type_impl_gen>
294				#frame_support::traits::IntegrityTest
295			for #pallet_ident<#type_use_gen> #where_clause
296			{
297				fn integrity_test() {
298					#frame_support::__private::sp_io::TestExternalities::default().execute_with(|| {
299						<
300							Self as #frame_support::traits::Hooks<
301								#frame_system::pallet_prelude::BlockNumberFor::<T>
302							>
303						>::integrity_test()
304					});
305				}
306			}
307		}
308
309		#frame_support::try_runtime_enabled! {
310			impl<#type_impl_gen>
311				#frame_support::traits::TryState<#frame_system::pallet_prelude::BlockNumberFor::<T>>
312				for #pallet_ident<#type_use_gen> #where_clause
313			{
314				fn try_state(
315					n: #frame_system::pallet_prelude::BlockNumberFor::<T>,
316					_s: #frame_support::traits::TryStateSelect
317				) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> {
318					#frame_support::__private::log::info!(
319						target: #frame_support::LOG_TARGET,
320						"🩺 Running {:?} try-state checks",
321						#pallet_name,
322					);
323					<
324						Self as #frame_support::traits::Hooks<
325							#frame_system::pallet_prelude::BlockNumberFor::<T>
326						>
327					>::try_state(n).inspect_err(|err| {
328						#frame_support::__private::log::error!(
329							target: #frame_support::LOG_TARGET,
330							"❌ {:?} try_state checks failed: {:?}",
331							#pallet_name,
332							err
333						);
334					})
335				}
336			}
337		}
338	)
339}