referrerpolicy=no-referrer-when-downgrade

polkadot_omni_node_lib/common/
runtime.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Runtime parameters.
18
19use 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
28/// Expected parachain system pallet runtime type name.
29pub const DEFAULT_PARACHAIN_SYSTEM_PALLET_NAME: &str = "ParachainSystem";
30/// Expected frame system pallet runtime type name.
31pub const DEFAULT_FRAME_SYSTEM_PALLET_NAME: &str = "System";
32
33/// The Aura ID used by the Aura consensus
34#[derive(PartialEq)]
35pub enum AuraConsensusId {
36	/// Ed25519
37	Ed25519,
38	/// Sr25519
39	Sr25519,
40}
41
42/// The choice of consensus for the parachain omni-node.
43#[derive(PartialEq)]
44pub enum Consensus {
45	/// Aura consensus.
46	Aura(AuraConsensusId),
47}
48
49/// The choice of block number for the parachain omni-node.
50#[derive(PartialEq, Debug)]
51pub enum BlockNumber {
52	/// u32
53	U32,
54	/// u64
55	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/// Helper enum listing the supported Runtime types
87#[derive(PartialEq)]
88pub enum Runtime {
89	/// None of the system-chain runtimes, rather the node will act agnostic to the runtime ie. be
90	/// an omni-node, and simply run a node with the given consensus algorithm.
91	Omni(BlockNumber, Consensus),
92}
93
94/// Helper trait used for extracting the Runtime variant from the chain spec ID.
95pub trait RuntimeResolver {
96	/// Extract the Runtime variant from the chain spec ID.
97	fn runtime(&self, chain_spec: &dyn ChainSpec) -> sc_cli::Result<Runtime>;
98}
99
100/// Default implementation for `RuntimeResolver` that just returns
101/// `Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519))`.
102pub 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}