frame_support/traits/try_runtime/
mod.rs
1pub 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}
44
45impl Select {
46 pub fn any(&self) -> bool {
48 !matches!(self, Select::None)
49 }
50}
51
52impl Default for Select {
53 fn default() -> Self {
54 Select::None
55 }
56}
57
58impl core::fmt::Debug for Select {
59 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
60 match self {
61 Select::RoundRobin(x) => write!(f, "RoundRobin({})", x),
62 Select::Only(x) => write!(
63 f,
64 "Only({:?})",
65 x.iter()
66 .map(|x| alloc::str::from_utf8(x).unwrap_or("<invalid?>"))
67 .collect::<Vec<_>>(),
68 ),
69 Select::All => write!(f, "All"),
70 Select::None => write!(f, "None"),
71 }
72 }
73}
74
75#[cfg(feature = "std")]
76impl std::str::FromStr for Select {
77 type Err = &'static str;
78 fn from_str(s: &str) -> Result<Self, Self::Err> {
79 match s {
80 "all" | "All" => Ok(Select::All),
81 "none" | "None" => Ok(Select::None),
82 _ =>
83 if s.starts_with("rr-") {
84 let count = s
85 .split_once('-')
86 .and_then(|(_, count)| count.parse::<u32>().ok())
87 .ok_or("failed to parse count")?;
88 Ok(Select::RoundRobin(count))
89 } else {
90 let pallets = s.split(',').map(|x| x.as_bytes().to_vec()).collect::<Vec<_>>();
91 Ok(Select::Only(pallets))
92 },
93 }
94 }
95}
96
97#[derive(codec::Encode, codec::Decode, Clone, Debug, Copy, scale_info::TypeInfo, PartialEq)]
99pub enum UpgradeCheckSelect {
100 None,
102 All,
104 PreAndPost,
106 TryState,
108}
109
110impl UpgradeCheckSelect {
111 pub fn pre_and_post(&self) -> bool {
113 matches!(self, Self::All | Self::PreAndPost)
114 }
115
116 pub fn try_state(&self) -> bool {
118 matches!(self, Self::All | Self::TryState)
119 }
120
121 pub fn any(&self) -> bool {
123 !matches!(self, Self::None)
124 }
125}
126
127#[cfg(feature = "std")]
128impl core::str::FromStr for UpgradeCheckSelect {
129 type Err = &'static str;
130
131 fn from_str(s: &str) -> Result<Self, Self::Err> {
132 match s.to_lowercase().as_str() {
133 "none" => Ok(Self::None),
134 "all" => Ok(Self::All),
135 "pre-and-post" => Ok(Self::PreAndPost),
136 "try-state" => Ok(Self::TryState),
137 _ => Err("Invalid CheckSelector"),
138 }
139 }
140}
141
142pub trait TryState<BlockNumber> {
149 fn try_state(_: BlockNumber, _: Select) -> Result<(), TryRuntimeError>;
151}
152
153#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
154#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
155#[cfg_attr(all(feature = "tuples-128"), impl_for_tuples(128))]
156impl<BlockNumber: Clone + core::fmt::Debug + AtLeast32BitUnsigned> TryState<BlockNumber> for Tuple {
157 for_tuples!( where #( Tuple: crate::traits::PalletInfoAccess )* );
158 fn try_state(n: BlockNumber, targets: Select) -> Result<(), TryRuntimeError> {
159 match targets {
160 Select::None => Ok(()),
161 Select::All => {
162 let mut errors = Vec::<TryRuntimeError>::new();
163
164 for_tuples!(#(
165 if let Err(err) = Tuple::try_state(n.clone(), targets.clone()) {
166 errors.push(err);
167 }
168 )*);
169
170 if !errors.is_empty() {
171 log::error!(
172 target: "try-runtime",
173 "Detected errors while executing `try_state`:",
174 );
175
176 errors.iter().for_each(|err| {
177 log::error!(
178 target: "try-runtime",
179 "{:?}",
180 err
181 );
182 });
183
184 return Err(
185 "Detected errors while executing `try_state` checks. See logs for more \
186 info."
187 .into(),
188 )
189 }
190
191 Ok(())
192 },
193 Select::RoundRobin(len) => {
194 let functions: &[fn(BlockNumber, Select) -> Result<(), TryRuntimeError>] =
195 &[for_tuples!(#( Tuple::try_state ),*)];
196 let skip = n.clone() % (functions.len() as u32).into();
197 let skip: u32 =
198 skip.try_into().unwrap_or_else(|_| sp_runtime::traits::Bounded::max_value());
199 let mut result = Ok(());
200 for try_state_fn in functions.iter().cycle().skip(skip as usize).take(len as usize)
201 {
202 result = result.and(try_state_fn(n.clone(), targets.clone()));
203 }
204 result
205 },
206 Select::Only(ref pallet_names) => {
207 let try_state_fns: &[(
208 &'static str,
209 fn(BlockNumber, Select) -> Result<(), TryRuntimeError>,
210 )] = &[for_tuples!(
211 #( (<Tuple as crate::traits::PalletInfoAccess>::name(), Tuple::try_state) ),*
212 )];
213 let mut result = Ok(());
214 pallet_names.iter().for_each(|pallet_name| {
215 if let Some((name, try_state_fn)) =
216 try_state_fns.iter().find(|(name, _)| name.as_bytes() == pallet_name)
217 {
218 result = result.and(try_state_fn(n.clone(), targets.clone()));
219 } else {
220 log::warn!(
221 "Pallet {:?} not found",
222 alloc::str::from_utf8(pallet_name).unwrap_or_default()
223 );
224 }
225 });
226
227 result
228 },
229 }
230 }
231}