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
117#[derive(codec::Encode, codec::Decode, Clone, Debug, Copy, scale_info::TypeInfo, PartialEq)]
119pub enum UpgradeCheckSelect {
120 None,
122 All,
124 PreAndPost,
126 TryState,
128}
129
130impl UpgradeCheckSelect {
131 pub fn pre_and_post(&self) -> bool {
133 matches!(self, Self::All | Self::PreAndPost)
134 }
135
136 pub fn try_state(&self) -> bool {
138 matches!(self, Self::All | Self::TryState)
139 }
140
141 pub fn any(&self) -> bool {
143 !matches!(self, Self::None)
144 }
145}
146
147#[cfg(feature = "std")]
148impl core::str::FromStr for UpgradeCheckSelect {
149 type Err = &'static str;
150
151 fn from_str(s: &str) -> Result<Self, Self::Err> {
152 match s.to_lowercase().as_str() {
153 "none" => Ok(Self::None),
154 "all" => Ok(Self::All),
155 "pre-and-post" => Ok(Self::PreAndPost),
156 "try-state" => Ok(Self::TryState),
157 _ => Err("Invalid CheckSelector"),
158 }
159 }
160}
161
162pub trait TryState<BlockNumber> {
169 fn try_state(_: BlockNumber, _: Select) -> Result<(), TryRuntimeError>;
171}
172
173#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
174#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
175#[cfg_attr(all(feature = "tuples-128"), impl_for_tuples(128))]
176impl<BlockNumber: Clone + core::fmt::Debug + AtLeast32BitUnsigned> TryState<BlockNumber> for Tuple {
177 for_tuples!( where #( Tuple: crate::traits::PalletInfoAccess )* );
178 fn try_state(n: BlockNumber, targets: Select) -> Result<(), TryRuntimeError> {
179 match targets {
180 Select::None => Ok(()),
181 Select::All => {
182 let mut errors = Vec::<TryRuntimeError>::new();
183
184 for_tuples!(#(
185 if let Err(err) = Tuple::try_state(n.clone(), targets.clone()) {
186 errors.push(err);
187 }
188 )*);
189
190 if !errors.is_empty() {
191 log::error!(
192 target: "try-runtime",
193 "Detected errors while executing `try_state`:",
194 );
195
196 errors.iter().for_each(|err| {
197 log::error!(
198 target: "try-runtime",
199 "{:?}",
200 err
201 );
202 });
203
204 return Err(
205 "Detected errors while executing `try_state` checks. See logs for more \
206 info."
207 .into(),
208 );
209 }
210
211 Ok(())
212 },
213 Select::RoundRobin(len) => {
214 let functions: &[fn(BlockNumber, Select) -> Result<(), TryRuntimeError>] =
215 &[for_tuples!(#( Tuple::try_state ),*)];
216 let skip = n.clone() % (functions.len() as u32).into();
217 let skip: u32 =
218 skip.try_into().unwrap_or_else(|_| sp_runtime::traits::Bounded::max_value());
219 let mut result = Ok(());
220 for try_state_fn in functions.iter().cycle().skip(skip as usize).take(len as usize)
221 {
222 result = result.and(try_state_fn(n.clone(), targets.clone()));
223 }
224 result
225 },
226 Select::Only(ref pallet_names) => {
227 let try_state_fns: &[(
228 &'static str,
229 fn(BlockNumber, Select) -> Result<(), TryRuntimeError>,
230 )] = &[for_tuples!(
231 #( (<Tuple as crate::traits::PalletInfoAccess>::name(), Tuple::try_state) ),*
232 )];
233 let mut result = Ok(());
234 pallet_names.iter().for_each(|pallet_name| {
235 if let Some((name, try_state_fn)) =
236 try_state_fns.iter().find(|(name, _)| name.as_bytes() == pallet_name)
237 {
238 result = result.and(try_state_fn(n.clone(), targets.clone()));
239 } else {
240 log::warn!(
241 "Pallet {:?} not found",
242 alloc::str::from_utf8(pallet_name).unwrap_or_default()
243 );
244 }
245 });
246
247 result
248 },
249 Select::AllExcept(ref excluded_pallet_names) => {
250 let try_state_fns: &[(
251 &'static str,
252 fn(BlockNumber, Select) -> Result<(), TryRuntimeError>,
253 )] = &[for_tuples!(
254 #( (<Tuple as crate::traits::PalletInfoAccess>::name(), Tuple::try_state) ),*
255 )];
256
257 excluded_pallet_names.iter().for_each(|excluded_name| {
258 if !try_state_fns.iter().any(|(name, _)| name.as_bytes() == excluded_name) {
259 log::warn!(
260 "Pallet {:?} not found while trying to filter it out in Select::AllExcept",
261 alloc::str::from_utf8(excluded_name).unwrap_or_default()
262 );
263 }
264 });
265
266 let try_state_fns: Vec<_> = try_state_fns
267 .iter()
268 .filter(|(name, _)| {
269 !excluded_pallet_names
270 .iter()
271 .any(|excluded_name| name.as_bytes() == excluded_name)
272 })
273 .collect();
274
275 let mut errors = Vec::<TryRuntimeError>::new();
276
277 try_state_fns.iter().for_each(|(name, try_state_fn)| {
278 if let Err(err) = try_state_fn(n.clone(), targets.clone()) {
279 errors.push(err);
280 }
281 });
282
283 if !errors.is_empty() {
284 log::error!(
285 target: "try-runtime",
286 "Detected errors while executing `try_state`:",
287 );
288
289 errors.iter().for_each(|err| {
290 log::error!(
291 target: "try-runtime",
292 "{:?}",
293 err
294 );
295 });
296
297 return Err(
298 "Detected errors while executing `try_state` checks. See logs for more \
299 info."
300 .into(),
301 );
302 }
303
304 Ok(())
305 },
306 }
307 }
308}