polkadot_omni_node_lib/common/
runtime.rs
1use 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(PartialEq)]
35pub enum AuraConsensusId {
36 Ed25519,
38 Sr25519,
40}
41
42#[derive(PartialEq)]
44pub enum Consensus {
45 Aura(AuraConsensusId),
47}
48
49#[derive(PartialEq, Debug)]
51pub enum BlockNumber {
52 U32,
54 U64,
56}
57
58impl Display for BlockNumber {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 BlockNumber::U32 => write!(f, "u32"),
62 BlockNumber::U64 => write!(f, "u64"),
63 }
64 }
65}
66
67impl Into<TypeDefPrimitive> for BlockNumber {
68 fn into(self) -> TypeDefPrimitive {
69 match self {
70 BlockNumber::U32 => TypeDefPrimitive::U32,
71 BlockNumber::U64 => TypeDefPrimitive::U64,
72 }
73 }
74}
75
76impl BlockNumber {
77 fn from_type_def(type_def: &TypeDef<PortableForm>) -> Option<BlockNumber> {
78 match type_def {
79 TypeDef::Primitive(TypeDefPrimitive::U32) => Some(BlockNumber::U32),
80 TypeDef::Primitive(TypeDefPrimitive::U64) => Some(BlockNumber::U64),
81 _ => None,
82 }
83 }
84}
85
86#[derive(PartialEq)]
88pub enum Runtime {
89 Omni(BlockNumber, Consensus),
92}
93
94pub trait RuntimeResolver {
96 fn runtime(&self, chain_spec: &dyn ChainSpec) -> sc_cli::Result<Runtime>;
98}
99
100pub struct DefaultRuntimeResolver;
103
104impl RuntimeResolver for DefaultRuntimeResolver {
105 fn runtime(&self, chain_spec: &dyn ChainSpec) -> sc_cli::Result<Runtime> {
106 let Ok(metadata_inspector) = MetadataInspector::new(chain_spec) else {
107 log::info!("Unable to check metadata. Skipping metadata checks. Metadata checks are supported for metadata versions v14 and higher.");
108 return Ok(Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519)))
109 };
110
111 let block_number = match metadata_inspector.block_number() {
112 Some(inner) => inner,
113 None => {
114 log::warn!(
115 r#"⚠️ There isn't a runtime type named `System`, corresponding to the `frame-system`
116 pallet (https://docs.rs/frame-system/latest/frame_system/). Please check Omni Node docs for runtime conventions:
117 https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html#runtime-conventions.
118 Note: We'll assume a block number size of `u32`."#
119 );
120 BlockNumber::U32
121 },
122 };
123
124 if !metadata_inspector.pallet_exists(DEFAULT_PARACHAIN_SYSTEM_PALLET_NAME) {
125 log::warn!(
126 r#"⚠️ The parachain system pallet (https://docs.rs/crate/cumulus-pallet-parachain-system/latest) is
127 missing from the runtime’s metadata. Please check Omni Node docs for runtime conventions:
128 https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html#runtime-conventions."#
129 );
130 }
131
132 Ok(Runtime::Omni(block_number, Consensus::Aura(AuraConsensusId::Sr25519)))
133 }
134}
135
136struct MetadataInspector(Metadata);
137
138impl MetadataInspector {
139 fn new(chain_spec: &dyn ChainSpec) -> Result<MetadataInspector, sc_cli::Error> {
140 MetadataInspector::fetch_metadata(chain_spec).map(MetadataInspector)
141 }
142
143 fn pallet_exists(&self, name: &str) -> bool {
144 self.0.pallet_by_name(name).is_some()
145 }
146
147 fn block_number(&self) -> Option<BlockNumber> {
148 let pallet_metadata = self.0.pallet_by_name(DEFAULT_FRAME_SYSTEM_PALLET_NAME);
149 pallet_metadata
150 .and_then(|inner| inner.storage())
151 .and_then(|inner| inner.entry_by_name("Number"))
152 .and_then(|number_ty| match number_ty.entry_type() {
153 StorageEntryType::Plain(ty_id) => Some(ty_id),
154 _ => None,
155 })
156 .and_then(|ty_id| self.0.types().resolve(*ty_id))
157 .and_then(|portable_type| BlockNumber::from_type_def(&portable_type.type_def))
158 }
159
160 fn fetch_metadata(chain_spec: &dyn ChainSpec) -> Result<Metadata, sc_cli::Error> {
161 let mut storage = chain_spec.build_storage()?;
162 let code_bytes = storage
163 .top
164 .remove(sp_storage::well_known_keys::CODE)
165 .ok_or("chain spec genesis does not contain code")?;
166 let opaque_metadata = fetch_latest_metadata_from_code_blob(
167 &WasmExecutor::<ParachainHostFunctions>::builder()
168 .with_allow_missing_host_functions(true)
169 .build(),
170 sp_runtime::Cow::Borrowed(code_bytes.as_slice()),
171 )
172 .map_err(|err| err.to_string())?;
173
174 Metadata::decode(&mut (*opaque_metadata).as_slice()).map_err(Into::into)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use crate::runtime::{
181 BlockNumber, MetadataInspector, DEFAULT_FRAME_SYSTEM_PALLET_NAME,
182 DEFAULT_PARACHAIN_SYSTEM_PALLET_NAME,
183 };
184 use codec::Decode;
185 use cumulus_client_service::ParachainHostFunctions;
186 use sc_executor::WasmExecutor;
187 use sc_runtime_utilities::fetch_latest_metadata_from_code_blob;
188
189 fn cumulus_test_runtime_metadata() -> subxt_metadata::Metadata {
190 let opaque_metadata = fetch_latest_metadata_from_code_blob(
191 &WasmExecutor::<ParachainHostFunctions>::builder()
192 .with_allow_missing_host_functions(true)
193 .build(),
194 sp_runtime::Cow::Borrowed(cumulus_test_runtime::WASM_BINARY.unwrap()),
195 )
196 .unwrap();
197
198 subxt_metadata::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap()
199 }
200
201 #[test]
202 fn test_pallet_exists() {
203 let metadata_inspector = MetadataInspector(cumulus_test_runtime_metadata());
204 assert!(metadata_inspector.pallet_exists(DEFAULT_PARACHAIN_SYSTEM_PALLET_NAME));
205 assert!(metadata_inspector.pallet_exists(DEFAULT_FRAME_SYSTEM_PALLET_NAME));
206 }
207
208 #[test]
209 fn test_runtime_block_number() {
210 let metadata_inspector = MetadataInspector(cumulus_test_runtime_metadata());
211 assert_eq!(metadata_inspector.block_number().unwrap(), BlockNumber::U32);
212 }
213}