frame_support/traits/try_runtime/
mod.rs1pub mod decode_entire_state;
21pub use decode_entire_state::{TryDecodeEntireStorage, TryDecodeEntireStorageError};
22
23use super::StorageInstance;
24
25use alloc::vec::Vec;
26use impl_trait_for_tuples::impl_for_tuples;
27use sp_arithmetic::traits::AtLeast32BitUnsigned;
28use sp_runtime::TryRuntimeError;
29
30#[derive(codec::Encode, codec::Decode, Clone, scale_info::TypeInfo, PartialEq)]
32pub enum Select {
33 None,
35 All,
37 RoundRobin(u32),
39 Only(Vec<Vec<u8>>),
43 AllExcept(Vec<Vec<u8>>),
47}
48
49impl Select {
50 pub fn any(&self) -> bool {
52 !matches!(self, Select::None)
53 }
54}
55
56impl Default for Select {
57 fn default() -> Self {
58 Select::None
59 }
60}
61
62impl core::fmt::Debug for Select {
63 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64 match self {
65 Select::RoundRobin(x) => write!(f, "RoundRobin({})", x),
66 Select::Only(x) => write!(
67 f,
68 "Only({:?})",
69 x.iter()
70 .map(|x| alloc::str::from_utf8(x).unwrap_or("<invalid?>"))
71 .collect::<Vec<_>>(),
72 ),
73 Select::AllExcept(x) => write!(
74 f,
75 "AllExcept({:?})",
76 x.iter()
77 .map(|x| alloc::str::from_utf8(x).unwrap_or("<invalid?>"))
78 .collect::<Vec<_>>(),
79 ),
80 Select::All => write!(f, "All"),
81 Select::None => write!(f, "None"),
82 }
83 }
84}
85
86#[cfg(feature = "std")]
87impl std::str::FromStr for Select {
88 type Err = &'static str;
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
90 match s {
91 "all" | "All" => Ok(Select::All),
92 "none" | "None" => Ok(Select::None),
93 _ =>
94 if s.starts_with("rr-") {
95 let count = s
96 .split_once('-')
97 .and_then(|(_, count)| count.parse::<u32>().ok())
98 .ok_or("failed to parse count")?;
99 Ok(Select::RoundRobin(count))
100 } else if s.starts_with("all-except-") {
101 let pallets = s
102 .strip_prefix("all-except-")
103 .ok_or("failed to parse all-except prefix")?
104 .split(',')
105 .map(|x| x.as_bytes().to_vec())
106 .collect::<Vec<_>>();
107 Ok(Select::AllExcept(pallets))
108 } else {
109 let pallets = s.split(',').map(|x| x.as_bytes().to_vec()).collect::<Vec<_>>();
110 Ok(Select::Only(pallets))
111 },
112 }
113 }
114}
115
116#[derive(codec::Encode, codec::Decode, Clone, Debug, Copy, scale_info::TypeInfo, PartialEq)]
118pub enum UpgradeCheckSelect {
119 None,
121 All,
123 PreAndPost,
125 TryState,
127}
128
129impl UpgradeCheckSelect {
130 pub fn pre_and_post(&self) -> bool {
132 matches!(self, Self::All | Self::PreAndPost)
133 }
134
135 pub fn try_state(&self) -> bool {
137 matches!(self, Self::All | Self::TryState)
138 }
139
140 pub fn any(&self) -> bool {
142 !matches!(self, Self::None)
143 }
144}
145
146#[cfg(feature = "std")]
147impl core::str::FromStr for UpgradeCheckSelect {
148 type Err = &'static str;
149
150 fn from_str(s: &str) -> Result<Self, Self::Err> {
151 match s.to_lowercase().as_str() {
152 "none" => Ok(Self::None),
153 "all" => Ok(Self::All),
154 "pre-and-post" => Ok(Self::PreAndPost),
155 "try-state" => Ok(Self::TryState),
156 _ => Err("Invalid CheckSelector"),
157 }
158 }
159}
160
161pub trait TryState<BlockNumber> {
168 fn try_state(_: BlockNumber, _: Select) -> Result<(), TryRuntimeError>;
170}
171
172#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
173#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
174#[cfg_attr(all(feature = "tuples-128"), impl_for_tuples(128))]
175impl<BlockNumber: Clone + core::fmt::Debug + AtLeast32BitUnsigned> TryState<BlockNumber> for Tuple {
176 for_tuples!( where #( Tuple: crate::traits::PalletInfoAccess )* );
177 fn try_state(n: BlockNumber, targets: Select) -> Result<(), TryRuntimeError> {
178 match targets {
179 Select::None => Ok(()),
180 Select::All => {
181 let mut errors = Vec::<TryRuntimeError>::new();
182
183 for_tuples!(#(
184 if let Err(err) = Tuple::try_state(n.clone(), targets.clone()) {
185 errors.push(err);
186 }
187 )*);
188
189 if !errors.is_empty() {
190 log::error!(
191 target: "try-runtime",
192 "Detected errors while executing `try_state`:",
193 );
194
195 errors.iter().for_each(|err| {
196 log::error!(
197 target: "try-runtime",
198 "{:?}",
199 err
200 );
201 });
202
203 return Err(
204 "Detected errors while executing `try_state` checks. See logs for more \
205 info."
206 .into(),
207 )
208 }
209
210 Ok(())
211 },
212 Select::RoundRobin(len) => {
213 let functions: &[fn(BlockNumber, Select) -> Result<(), TryRuntimeError>] =
214 &[for_tuples!(#( Tuple::try_state ),*)];
215 let skip = n.clone() % (functions.len() as u32).into();
216 let skip: u32 =
217 skip.try_into().unwrap_or_else(|_| sp_runtime::traits::Bounded::max_value());
218 let mut result = Ok(());
219 for try_state_fn in functions.iter().cycle().skip(skip as usize).take(len as usize)
220 {
221 result = result.and(try_state_fn(n.clone(), targets.clone()));
222 }
223 result
224 },
225 Select::Only(ref pallet_names) => {
226 let try_state_fns: &[(
227 &'static str,
228 fn(BlockNumber, Select) -> Result<(), TryRuntimeError>,
229 )] = &[for_tuples!(
230 #( (<Tuple as crate::traits::PalletInfoAccess>::name(), Tuple::try_state) ),*
231 )];
232 let mut result = Ok(());
233 pallet_names.iter().for_each(|pallet_name| {
234 if let Some((name, try_state_fn)) =
235 try_state_fns.iter().find(|(name, _)| name.as_bytes() == pallet_name)
236 {
237 result = result.and(try_state_fn(n.clone(), targets.clone()));
238 } else {
239 log::warn!(
240 "Pallet {:?} not found",
241 alloc::str::from_utf8(pallet_name).unwrap_or_default()
242 );
243 }
244 });
245
246 result
247 },
248 Select::AllExcept(ref excluded_pallet_names) => {
249 let try_state_fns: &[(
250 &'static str,
251 fn(BlockNumber, Select) -> Result<(), TryRuntimeError>,
252 )] = &[for_tuples!(
253 #( (<Tuple as crate::traits::PalletInfoAccess>::name(), Tuple::try_state) ),*
254 )];
255
256 excluded_pallet_names.iter().for_each(|excluded_name| {
257 if !try_state_fns.iter().any(|(name, _)| name.as_bytes() == excluded_name) {
258 log::warn!(
259 "Pallet {:?} not found while trying to filter it out in Select::AllExcept",
260 alloc::str::from_utf8(excluded_name).unwrap_or_default()
261 );
262 }
263 });
264
265 let try_state_fns: Vec<_> = try_state_fns
266 .iter()
267 .filter(|(name, _)| {
268 !excluded_pallet_names
269 .iter()
270 .any(|excluded_name| name.as_bytes() == excluded_name)
271 })
272 .collect();
273
274 let mut errors = Vec::<TryRuntimeError>::new();
275
276 try_state_fns.iter().for_each(|(name, try_state_fn)| {
277 if let Err(err) = try_state_fn(n.clone(), targets.clone()) {
278 errors.push(err);
279 }
280 });
281
282 if !errors.is_empty() {
283 log::error!(
284 target: "try-runtime",
285 "Detected errors while executing `try_state`:",
286 );
287
288 errors.iter().for_each(|err| {
289 log::error!(
290 target: "try-runtime",
291 "{:?}",
292 err
293 );
294 });
295
296 return Err(
297 "Detected errors while executing `try_state` checks. See logs for more \
298 info."
299 .into(),
300 )
301 }
302
303 Ok(())
304 },
305 }
306 }
307}