polkadot_omni_node_lib/common/
runtime.rs1use codec::Decode;
20use cumulus_client_service::ParachainHostFunctions;
21use sc_chain_spec::ChainSpec;
22use sc_executor::WasmExecutor;
23use sc_runtime_utilities::fetch_latest_metadata_from_code_blob;
24use scale_info::{form::PortableForm, TypeDef, TypeDefPrimitive};
25use std::fmt::Display;
26use subxt_metadata::{Metadata, StorageEntryType};
27
28pub const DEFAULT_PARACHAIN_SYSTEM_PALLET_NAME: &str = "ParachainSystem";
30pub const DEFAULT_FRAME_SYSTEM_PALLET_NAME: &str = "System";
32
33#[derive(Debug, PartialEq)]
35pub enum AuraConsensusId {
36 Ed25519,
38 Sr25519,
40}
41
42pub fn aura_id_from_chain_spec_id(id: &str) -> AuraConsensusId {
53 let id_normalized = id.replace('_', "-");
54 if id_normalized.starts_with("asset-hub-polkadot") || id_normalized.starts_with("statemint") {
55 log::warn!(
56 "⚠️ Aura authority id type is assumed to be `ed25519` because the chain spec id \
57 starts with `asset-hub-polkadot` or `statemint`. This is a known special case for \
58 Asset Hub Polkadot (formerly Statemint). If this assumption is wrong for your runtime, \
59 the node may not work correctly."
60 );
61 AuraConsensusId::Ed25519
62 } else {
63 log::warn!(
64 "⚠️ Aura authority id type is assumed to be `sr25519` by default. Runtimes using \
65 `ed25519` for Aura are not yet supported (except for `asset-hub-polkadot` / `statemint`). \
66 If your runtime uses `ed25519` for Aura, it may not work correctly with this node."
67 );
68 AuraConsensusId::Sr25519
69 }
70}
71
72#[derive(PartialEq)]
74pub enum Consensus {
75 Aura(AuraConsensusId),
77}
78
79#[derive(PartialEq, Debug)]
81pub enum BlockNumber {
82 U32,
84 U64,
86}
87
88impl Display for BlockNumber {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 match self {
91 BlockNumber::U32 => write!(f, "u32"),
92 BlockNumber::U64 => write!(f, "u64"),
93 }
94 }
95}
96
97impl Into<TypeDefPrimitive> for BlockNumber {
98 fn into(self) -> TypeDefPrimitive {
99 match self {
100 BlockNumber::U32 => TypeDefPrimitive::U32,
101 BlockNumber::U64 => TypeDefPrimitive::U64,
102 }
103 }
104}
105
106impl BlockNumber {
107 fn from_type_def(type_def: &TypeDef<PortableForm>) -> Option<BlockNumber> {
108 match type_def {
109 TypeDef::Primitive(TypeDefPrimitive::U32) => Some(BlockNumber::U32),
110 TypeDef::Primitive(TypeDefPrimitive::U64) => Some(BlockNumber::U64),
111 _ => None,
112 }
113 }
114}
115
116#[derive(PartialEq)]
118pub enum Runtime {
119 Omni(BlockNumber, Consensus),
122}
123
124pub trait RuntimeResolver {
126 fn runtime(&self, chain_spec: &dyn ChainSpec) -> sc_cli::Result<Runtime>;
128}
129
130pub struct DefaultRuntimeResolver;
133
134impl RuntimeResolver for DefaultRuntimeResolver {
135 fn runtime(&self, chain_spec: &dyn ChainSpec) -> sc_cli::Result<Runtime> {
136 let aura_id = aura_id_from_chain_spec_id(chain_spec.id());
137
138 let Ok(metadata_inspector) = MetadataInspector::new(chain_spec) else {
139 log::info!("Unable to check metadata. Skipping metadata checks. Metadata checks are supported for metadata versions v14 and higher.");
140 return Ok(Runtime::Omni(BlockNumber::U32, Consensus::Aura(aura_id)));
141 };
142
143 let block_number = metadata_inspector.block_number().unwrap_or_else(|| {
144 log::warn!(
145 r#"⚠️ There isn't a runtime type named `System`, corresponding to the `frame-system`
146 pallet (https://docs.rs/frame-system/latest/frame_system/). Please check Omni Node docs for runtime conventions:
147 https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html#runtime-conventions.
148 Note: We'll assume a block number size of `u32`."#
149 );
150 BlockNumber::U32
151 });
152
153 if !metadata_inspector.pallet_exists(DEFAULT_PARACHAIN_SYSTEM_PALLET_NAME) {
154 log::warn!(
155 r#"⚠️ The parachain system pallet (https://docs.rs/crate/cumulus-pallet-parachain-system/latest) is
156 missing from the runtime's metadata. Please check Omni Node docs for runtime conventions:
157 https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html#runtime-conventions."#
158 );
159 }
160
161 Ok(Runtime::Omni(block_number, Consensus::Aura(aura_id)))
162 }
163}
164
165struct MetadataInspector(Metadata);
166
167impl MetadataInspector {
168 fn new(chain_spec: &dyn ChainSpec) -> Result<MetadataInspector, sc_cli::Error> {
169 MetadataInspector::fetch_metadata(chain_spec).map(MetadataInspector)
170 }
171
172 fn pallet_exists(&self, name: &str) -> bool {
173 self.0.pallet_by_name(name).is_some()
174 }
175
176 fn block_number(&self) -> Option<BlockNumber> {
177 let pallet_metadata = self.0.pallet_by_name(DEFAULT_FRAME_SYSTEM_PALLET_NAME);
178 pallet_metadata
179 .and_then(|inner| inner.storage())
180 .and_then(|inner| inner.entry_by_name("Number"))
181 .and_then(|number_ty| match number_ty.entry_type() {
182 StorageEntryType::Plain(ty_id) => Some(ty_id),
183 _ => None,
184 })
185 .and_then(|ty_id| self.0.types().resolve(*ty_id))
186 .and_then(|portable_type| BlockNumber::from_type_def(&portable_type.type_def))
187 }
188
189 fn fetch_metadata(chain_spec: &dyn ChainSpec) -> Result<Metadata, sc_cli::Error> {
190 let mut storage = chain_spec.build_storage()?;
191 let code_bytes = storage
192 .top
193 .remove(sp_storage::well_known_keys::CODE)
194 .ok_or("chain spec genesis does not contain code")?;
195 let opaque_metadata = fetch_latest_metadata_from_code_blob(
196 &WasmExecutor::<ParachainHostFunctions>::builder()
197 .with_allow_missing_host_functions(true)
198 .build(),
199 sp_runtime::Cow::Borrowed(code_bytes.as_slice()),
200 )
201 .map_err(|err| err.to_string())?;
202
203 Metadata::decode(&mut (*opaque_metadata).as_slice()).map_err(Into::into)
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use crate::runtime::{
210 BlockNumber, MetadataInspector, DEFAULT_FRAME_SYSTEM_PALLET_NAME,
211 DEFAULT_PARACHAIN_SYSTEM_PALLET_NAME,
212 };
213 use codec::Decode;
214 use cumulus_client_service::ParachainHostFunctions;
215 use sc_executor::WasmExecutor;
216 use sc_runtime_utilities::fetch_latest_metadata_from_code_blob;
217
218 fn cumulus_test_runtime_metadata() -> subxt_metadata::Metadata {
219 let opaque_metadata = fetch_latest_metadata_from_code_blob(
220 &WasmExecutor::<ParachainHostFunctions>::builder()
221 .with_allow_missing_host_functions(true)
222 .build(),
223 sp_runtime::Cow::Borrowed(cumulus_test_runtime::WASM_BINARY.unwrap()),
224 )
225 .unwrap();
226
227 subxt_metadata::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap()
228 }
229
230 #[test]
231 fn test_pallet_exists() {
232 let metadata_inspector = MetadataInspector(cumulus_test_runtime_metadata());
233 assert!(metadata_inspector.pallet_exists(DEFAULT_PARACHAIN_SYSTEM_PALLET_NAME));
234 assert!(metadata_inspector.pallet_exists(DEFAULT_FRAME_SYSTEM_PALLET_NAME));
235 }
236
237 #[test]
238 fn test_runtime_block_number() {
239 let metadata_inspector = MetadataInspector(cumulus_test_runtime_metadata());
240 assert_eq!(metadata_inspector.block_number().unwrap(), BlockNumber::U32);
241 }
242
243 #[test]
244 fn test_aura_id_from_chain_spec_id() {
245 use crate::runtime::{aura_id_from_chain_spec_id, AuraConsensusId};
246
247 assert_eq!(aura_id_from_chain_spec_id("asset-hub-polkadot"), AuraConsensusId::Ed25519);
249 assert_eq!(aura_id_from_chain_spec_id("statemint"), AuraConsensusId::Ed25519);
250
251 assert_eq!(aura_id_from_chain_spec_id("asset-hub-kusama"), AuraConsensusId::Sr25519);
253 assert_eq!(aura_id_from_chain_spec_id("penpal-rococo-1000"), AuraConsensusId::Sr25519);
254 assert_eq!(aura_id_from_chain_spec_id("collectives-westend"), AuraConsensusId::Sr25519);
255 }
256}