referrerpolicy=no-referrer-when-downgrade

frame_benchmarking_cli/overhead/
command.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Contains the [`OverheadCmd`] as entry point for the CLI to execute
19//! the *overhead* benchmarks.
20
21use crate::{
22	extrinsic::{
23		bench::{Benchmark, BenchmarkParams as ExtrinsicBenchmarkParams},
24		ExtrinsicBuilder,
25	},
26	overhead::{
27		command::ChainType::{Parachain, Relaychain, Unknown},
28		fake_runtime_api,
29		remark_builder::SubstrateRemarkBuilder,
30		template::TemplateData,
31	},
32	shared::{
33		genesis_state,
34		genesis_state::{GenesisStateHandler, SpecGenesisSource},
35		HostInfoParams, WeightParams,
36	},
37};
38use clap::{error::ErrorKind, Args, CommandFactory, Parser};
39use codec::{Decode, Encode};
40use cumulus_client_parachain_inherent::MockValidationDataInherentDataProvider;
41use fake_runtime_api::RuntimeApi as FakeRuntimeApi;
42use frame_support::Deserialize;
43use genesis_state::WARN_SPEC_GENESIS_CTOR;
44use log::info;
45use polkadot_parachain_primitives::primitives::Id as ParaId;
46use sc_block_builder::BlockBuilderApi;
47use sc_chain_spec::{ChainSpec, ChainSpecExtension, GenesisBlockBuilder};
48use sc_cli::{CliConfiguration, Database, ImportParams, Result, SharedParams};
49use sc_client_api::{execution_extensions::ExecutionExtensions, UsageProvider};
50use sc_client_db::{BlocksPruning, DatabaseSettings};
51use sc_executor::WasmExecutor;
52use sc_runtime_utilities::fetch_latest_metadata_from_code_blob;
53use sc_service::{new_client, new_db_backend, BasePath, ClientConfig, TFullClient, TaskManager};
54use serde::Serialize;
55use serde_json::{json, Value};
56use sp_api::{ApiExt, CallApiAt, Core, ProvideRuntimeApi};
57use sp_blockchain::HeaderBackend;
58use sp_core::H256;
59use sp_inherents::{InherentData, InherentDataProvider};
60use sp_runtime::{
61	generic,
62	traits::{BlakeTwo256, Block as BlockT},
63	DigestItem, OpaqueExtrinsic,
64};
65use sp_storage::Storage;
66use sp_wasm_interface::HostFunctions;
67use std::{
68	fmt::{Debug, Display, Formatter},
69	fs,
70	path::PathBuf,
71	sync::Arc,
72};
73use subxt::{client::RuntimeVersion, ext::futures, Metadata};
74
75const DEFAULT_PARA_ID: u32 = 100;
76const LOG_TARGET: &'static str = "polkadot_sdk_frame::benchmark::overhead";
77
78/// Benchmark the execution overhead per-block and per-extrinsic.
79#[derive(Debug, Parser)]
80pub struct OverheadCmd {
81	#[allow(missing_docs)]
82	#[clap(flatten)]
83	pub shared_params: SharedParams,
84
85	#[allow(missing_docs)]
86	#[clap(flatten)]
87	pub import_params: ImportParams,
88
89	#[allow(missing_docs)]
90	#[clap(flatten)]
91	pub params: OverheadParams,
92}
93
94/// Configures the benchmark, the post-processing and weight generation.
95#[derive(Debug, Default, Serialize, Clone, PartialEq, Args)]
96pub struct OverheadParams {
97	#[allow(missing_docs)]
98	#[clap(flatten)]
99	pub weight: WeightParams,
100
101	#[allow(missing_docs)]
102	#[clap(flatten)]
103	pub bench: ExtrinsicBenchmarkParams,
104
105	#[allow(missing_docs)]
106	#[clap(flatten)]
107	pub hostinfo: HostInfoParams,
108
109	/// Add a header to the generated weight output file.
110	///
111	/// Good for adding LICENSE headers.
112	#[arg(long, value_name = "PATH")]
113	pub header: Option<PathBuf>,
114
115	/// Enable the Trie cache.
116	///
117	/// This should only be used for performance analysis and not for final results.
118	#[arg(long)]
119	pub enable_trie_cache: bool,
120
121	/// Optional runtime blob to use instead of the one from the genesis config.
122	#[arg(
123		long,
124		value_name = "PATH",
125		conflicts_with = "chain",
126		required_if_eq("genesis_builder", "runtime")
127	)]
128	pub runtime: Option<PathBuf>,
129
130	/// The preset that we expect to find in the GenesisBuilder runtime API.
131	///
132	/// This can be useful when a runtime has a dedicated benchmarking preset instead of using the
133	/// default one.
134	#[arg(long, default_value = sp_genesis_builder::DEV_RUNTIME_PRESET)]
135	pub genesis_builder_preset: String,
136
137	/// How to construct the genesis state.
138	///
139	/// Can be used together with `--chain` to determine whether the
140	/// genesis state should be initialized with the values from the
141	/// provided chain spec or a runtime-provided genesis preset.
142	#[arg(long, value_enum, alias = "genesis-builder-policy")]
143	pub genesis_builder: Option<GenesisBuilderPolicy>,
144
145	/// Parachain Id to use for parachains. If not specified, the benchmark code will choose
146	/// a para-id and patch the state accordingly.
147	#[arg(long)]
148	pub para_id: Option<u32>,
149}
150
151/// How the genesis state for benchmarking should be built.
152#[derive(clap::ValueEnum, Debug, Eq, PartialEq, Clone, Copy, Serialize)]
153#[clap(rename_all = "kebab-case")]
154pub enum GenesisBuilderPolicy {
155	/// Let the runtime build the genesis state through its `BuildGenesisConfig` runtime API.
156	/// This will use the `development` preset by default.
157	Runtime,
158	/// Use the runtime from the Spec file to build the genesis state.
159	SpecRuntime,
160	/// Use the spec file to build the genesis state. This fails when there is no spec.
161	#[value(alias = "spec")]
162	SpecGenesis,
163}
164
165/// Type of a benchmark.
166#[derive(Serialize, Clone, PartialEq, Copy)]
167pub(crate) enum BenchmarkType {
168	/// Measure the per-extrinsic execution overhead.
169	Extrinsic,
170	/// Measure the per-block execution overhead.
171	Block,
172}
173
174/// Hostfunctions that are typically used by parachains.
175pub type ParachainHostFunctions = (
176	cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
177	sp_io::SubstrateHostFunctions,
178);
179
180pub type BlockNumber = u32;
181
182/// Typical block header.
183pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
184
185/// Typical block type using `OpaqueExtrinsic`.
186pub type OpaqueBlock = generic::Block<Header, OpaqueExtrinsic>;
187
188/// Client type used throughout the benchmarking code.
189type OverheadClient<Block, HF> = TFullClient<Block, FakeRuntimeApi, WasmExecutor<HF>>;
190
191/// Creates inherent data for a given parachain ID.
192///
193/// This function constructs the inherent data required for block execution,
194/// including the relay chain state and validation data. Not all of these
195/// inherents are required for every chain. The runtime will pick the ones
196/// it requires based on their identifier.
197fn create_inherent_data<Client: UsageProvider<Block> + HeaderBackend<Block>, Block: BlockT>(
198	client: &Arc<Client>,
199	chain_type: &ChainType,
200) -> InherentData {
201	let genesis = client.usage_info().chain.best_hash;
202	let header = client.header(genesis).unwrap().unwrap();
203
204	let mut inherent_data = InherentData::new();
205
206	// Para inherent can only makes sense when we are handling a parachain.
207	if let Parachain(para_id) = chain_type {
208		let parachain_validation_data_provider = MockValidationDataInherentDataProvider::<()> {
209			para_id: ParaId::from(*para_id),
210			current_para_block_head: Some(header.encode().into()),
211			relay_offset: 1,
212			..Default::default()
213		};
214		let _ = futures::executor::block_on(
215			parachain_validation_data_provider.provide_inherent_data(&mut inherent_data),
216		);
217	}
218
219	// Parachain inherent that is used on relay chains to perform parachain validation.
220	let para_inherent = polkadot_primitives::InherentData {
221		bitfields: Vec::new(),
222		backed_candidates: Vec::new(),
223		disputes: Vec::new(),
224		parent_header: header,
225	};
226
227	// Timestamp inherent that is very common in substrate chains.
228	let timestamp = sp_timestamp::InherentDataProvider::new(std::time::Duration::default().into());
229
230	let _ = futures::executor::block_on(timestamp.provide_inherent_data(&mut inherent_data));
231	let _ =
232		inherent_data.put_data(polkadot_primitives::PARACHAINS_INHERENT_IDENTIFIER, &para_inherent);
233
234	inherent_data
235}
236
237/// Identifies what kind of chain we are dealing with.
238///
239/// Chains containing the `ParachainSystem` and `ParachainInfo` pallet are considered parachains.
240/// Chains containing the `ParaInherent` pallet are considered relay chains.
241fn identify_chain(metadata: &Metadata, para_id: Option<u32>) -> ChainType {
242	let parachain_info_exists = metadata.pallet_by_name("ParachainInfo").is_some();
243	let parachain_system_exists = metadata.pallet_by_name("ParachainSystem").is_some();
244	let para_inherent_exists = metadata.pallet_by_name("ParaInherent").is_some();
245
246	log::debug!("{} ParachainSystem", if parachain_system_exists { "✅" } else { "❌" });
247	log::debug!("{} ParachainInfo", if parachain_info_exists { "✅" } else { "❌" });
248	log::debug!("{} ParaInherent", if para_inherent_exists { "✅" } else { "❌" });
249
250	let chain_type = if parachain_system_exists && parachain_info_exists {
251		Parachain(para_id.unwrap_or(DEFAULT_PARA_ID))
252	} else if para_inherent_exists {
253		Relaychain
254	} else {
255		Unknown
256	};
257
258	log::info!(target: LOG_TARGET, "Identified Chain type from metadata: {}", chain_type);
259
260	chain_type
261}
262
263#[derive(Deserialize, Serialize, Clone, ChainSpecExtension)]
264pub struct ParachainExtension {
265	/// The id of the Parachain.
266	pub para_id: Option<u32>,
267}
268
269impl OverheadCmd {
270	fn state_handler_from_cli<HF: HostFunctions>(
271		&self,
272		chain_spec_from_api: Option<Box<dyn ChainSpec>>,
273	) -> Result<(GenesisStateHandler, Option<u32>)> {
274		let genesis_builder_to_source = || match self.params.genesis_builder {
275			Some(GenesisBuilderPolicy::Runtime) | Some(GenesisBuilderPolicy::SpecRuntime) =>
276				SpecGenesisSource::Runtime(self.params.genesis_builder_preset.clone()),
277			Some(GenesisBuilderPolicy::SpecGenesis) | None => {
278				log::warn!(target: LOG_TARGET, "{WARN_SPEC_GENESIS_CTOR}");
279				SpecGenesisSource::SpecJson
280			},
281		};
282
283		// First handle chain-spec passed in via API parameter.
284		if let Some(chain_spec) = chain_spec_from_api {
285			log::debug!(target: LOG_TARGET, "Initializing state handler with chain-spec from API: {:?}", chain_spec);
286
287			let source = genesis_builder_to_source();
288			return Ok((GenesisStateHandler::ChainSpec(chain_spec, source), self.params.para_id))
289		};
290
291		// Handle chain-spec passed in via CLI.
292		if let Some(chain_spec_path) = &self.shared_params.chain {
293			log::debug!(target: LOG_TARGET,
294				"Initializing state handler with chain-spec from path: {:?}",
295				chain_spec_path
296			);
297			let (chain_spec, para_id_from_chain_spec) =
298				genesis_state::chain_spec_from_path::<HF>(chain_spec_path.to_string().into())?;
299
300			let source = genesis_builder_to_source();
301
302			return Ok((
303				GenesisStateHandler::ChainSpec(chain_spec, source),
304				self.params.para_id.or(para_id_from_chain_spec),
305			))
306		};
307
308		// Check for runtimes. In general, we make sure that `--runtime` and `--chain` are
309		// incompatible on the CLI level.
310		if let Some(runtime_path) = &self.params.runtime {
311			log::debug!(target: LOG_TARGET, "Initializing state handler with runtime from path: {:?}", runtime_path);
312
313			let runtime_blob = fs::read(runtime_path)?;
314			return Ok((
315				GenesisStateHandler::Runtime(
316					runtime_blob,
317					Some(self.params.genesis_builder_preset.clone()),
318				),
319				self.params.para_id,
320			));
321		};
322
323		Err("Neither a runtime nor a chain-spec were specified".to_string().into())
324	}
325
326	fn check_args(
327		&self,
328		chain_spec: &Option<Box<dyn ChainSpec>>,
329	) -> std::result::Result<(), (ErrorKind, String)> {
330		if chain_spec.is_none() &&
331			self.params.runtime.is_none() &&
332			self.shared_params.chain.is_none()
333		{
334			return Err((
335				ErrorKind::MissingRequiredArgument,
336				"Provide either a runtime via `--runtime` or a chain spec via `--chain`"
337					.to_string(),
338			));
339		}
340
341		match self.params.genesis_builder {
342			Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::SpecRuntime) =>
343				if chain_spec.is_none() && self.shared_params.chain.is_none() {
344					return Err((
345						ErrorKind::MissingRequiredArgument,
346						"Provide a chain spec via `--chain`.".to_string(),
347					));
348				},
349			_ => {},
350		};
351		Ok(())
352	}
353
354	/// Run the overhead benchmark with the default extrinsic builder.
355	///
356	/// This will use [SubstrateRemarkBuilder] to build the extrinsic. It is
357	/// designed to match common configurations found in substrate chains.
358	pub fn run_with_default_builder_and_spec<Block, ExtraHF>(
359		&self,
360		chain_spec: Option<Box<dyn ChainSpec>>,
361	) -> Result<()>
362	where
363		Block: BlockT<Extrinsic = OpaqueExtrinsic, Hash = H256>,
364		ExtraHF: HostFunctions,
365	{
366		self.run_with_extrinsic_builder_and_spec::<Block, ExtraHF>(
367			Box::new(|metadata, hash, version| {
368				let genesis = subxt::utils::H256::from(hash.to_fixed_bytes());
369				Box::new(SubstrateRemarkBuilder::new(metadata, genesis, version)) as Box<_>
370			}),
371			chain_spec,
372		)
373	}
374
375	/// Run the benchmark overhead command.
376	///
377	/// The provided [ExtrinsicBuilder] will be used to build extrinsics for
378	/// block-building. It is expected that the provided implementation builds
379	/// a `System::remark` extrinsic.
380	pub fn run_with_extrinsic_builder_and_spec<Block, ExtraHF>(
381		&self,
382		ext_builder_provider: Box<
383			dyn FnOnce(Metadata, Block::Hash, RuntimeVersion) -> Box<dyn ExtrinsicBuilder>,
384		>,
385		chain_spec: Option<Box<dyn ChainSpec>>,
386	) -> Result<()>
387	where
388		Block: BlockT<Extrinsic = OpaqueExtrinsic>,
389		ExtraHF: HostFunctions,
390	{
391		if let Err((error_kind, msg)) = self.check_args(&chain_spec) {
392			let mut cmd = OverheadCmd::command();
393			cmd.error(error_kind, msg).exit();
394		};
395
396		let (state_handler, para_id) =
397			self.state_handler_from_cli::<(ParachainHostFunctions, ExtraHF)>(chain_spec)?;
398
399		let executor = WasmExecutor::<(ParachainHostFunctions, ExtraHF)>::builder()
400			.with_allow_missing_host_functions(true)
401			.build();
402
403		let opaque_metadata =
404			fetch_latest_metadata_from_code_blob(&executor, state_handler.get_code_bytes()?)
405				.map_err(|_| {
406					<&str as Into<sc_cli::Error>>::into("Unable to fetch latest stable metadata")
407				})?;
408		let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice())?;
409
410		// At this point we know what kind of chain we are dealing with.
411		let chain_type = identify_chain(&metadata, para_id);
412
413		// If we are dealing  with a parachain, make sure that the para id in genesis will
414		// match what we expect.
415		let genesis_patcher = match chain_type {
416			Parachain(para_id) =>
417				Some(Box::new(move |value| patch_genesis(value, Some(para_id))) as Box<_>),
418			_ => None,
419		};
420
421		let client = self.build_client_components::<Block, (ParachainHostFunctions, ExtraHF)>(
422			state_handler.build_storage::<(ParachainHostFunctions, ExtraHF)>(genesis_patcher)?,
423			executor,
424			&chain_type,
425		)?;
426
427		let inherent_data = create_inherent_data(&client, &chain_type);
428
429		let (ext_builder, runtime_name) = {
430			let genesis = client.usage_info().chain.best_hash;
431			let version = client.runtime_api().version(genesis).unwrap();
432			let runtime_name = version.spec_name;
433			let runtime_version = RuntimeVersion {
434				spec_version: version.spec_version,
435				transaction_version: version.transaction_version,
436			};
437
438			(ext_builder_provider(metadata, genesis, runtime_version), runtime_name)
439		};
440
441		self.run(
442			runtime_name.to_string(),
443			client,
444			inherent_data,
445			Default::default(),
446			&*ext_builder,
447			chain_type.requires_proof_recording(),
448		)
449	}
450
451	/// Run the benchmark overhead command.
452	pub fn run_with_extrinsic_builder<Block, ExtraHF>(
453		&self,
454		ext_builder_provider: Box<
455			dyn FnOnce(Metadata, Block::Hash, RuntimeVersion) -> Box<dyn ExtrinsicBuilder>,
456		>,
457	) -> Result<()>
458	where
459		Block: BlockT<Extrinsic = OpaqueExtrinsic>,
460		ExtraHF: HostFunctions,
461	{
462		self.run_with_extrinsic_builder_and_spec::<Block, ExtraHF>(ext_builder_provider, None)
463	}
464
465	fn build_client_components<Block, HF>(
466		&self,
467		genesis_storage: Storage,
468		executor: WasmExecutor<HF>,
469		chain_type: &ChainType,
470	) -> Result<Arc<OverheadClient<Block, HF>>>
471	where
472		Block: BlockT,
473		HF: HostFunctions,
474	{
475		let extensions = ExecutionExtensions::new(None, Arc::new(executor.clone()));
476
477		let base_path = match &self.shared_params.base_path {
478			None => BasePath::new_temp_dir()?,
479			Some(path) => BasePath::from(path.clone()),
480		};
481
482		let database_source = self.database_config(
483			&base_path.path().to_path_buf(),
484			self.database_cache_size()?.unwrap_or(1024),
485			self.database()?.unwrap_or(Database::Auto),
486		)?;
487
488		let backend = new_db_backend(DatabaseSettings {
489			trie_cache_maximum_size: self.trie_cache_maximum_size()?,
490			state_pruning: None,
491			blocks_pruning: BlocksPruning::KeepAll,
492			source: database_source,
493			metrics_registry: None,
494		})?;
495
496		let genesis_block_builder = GenesisBlockBuilder::new_with_storage(
497			genesis_storage,
498			true,
499			backend.clone(),
500			executor.clone(),
501		)?;
502
503		let tokio_runtime = sc_cli::build_runtime()?;
504		let task_manager = TaskManager::new(tokio_runtime.handle().clone(), None)
505			.map_err(|_| "Unable to build task manager")?;
506
507		let client: Arc<OverheadClient<Block, HF>> = Arc::new(new_client(
508			backend.clone(),
509			executor,
510			genesis_block_builder,
511			Default::default(),
512			Default::default(),
513			extensions,
514			Box::new(task_manager.spawn_handle()),
515			None,
516			None,
517			ClientConfig {
518				offchain_worker_enabled: false,
519				offchain_indexing_api: false,
520				wasm_runtime_overrides: None,
521				no_genesis: false,
522				wasm_runtime_substitutes: Default::default(),
523				enable_import_proof_recording: chain_type.requires_proof_recording(),
524			},
525		)?);
526
527		Ok(client)
528	}
529
530	/// Measure the per-block and per-extrinsic execution overhead.
531	///
532	/// Writes the results to console and into two instances of the
533	/// `weights.hbs` template, one for each benchmark.
534	pub fn run<Block, C>(
535		&self,
536		chain_name: String,
537		client: Arc<C>,
538		inherent_data: sp_inherents::InherentData,
539		digest_items: Vec<DigestItem>,
540		ext_builder: &dyn ExtrinsicBuilder,
541		should_record_proof: bool,
542	) -> Result<()>
543	where
544		Block: BlockT<Extrinsic = OpaqueExtrinsic>,
545		C: ProvideRuntimeApi<Block>
546			+ CallApiAt<Block>
547			+ UsageProvider<Block>
548			+ sp_blockchain::HeaderBackend<Block>,
549		C::Api: ApiExt<Block> + BlockBuilderApi<Block>,
550	{
551		if ext_builder.pallet() != "system" || ext_builder.extrinsic() != "remark" {
552			return Err(format!("The extrinsic builder is required to build `System::Remark` extrinsics but builds `{}` extrinsics instead", ext_builder.name()).into());
553		}
554
555		let bench = Benchmark::new(
556			client,
557			self.params.bench.clone(),
558			inherent_data,
559			digest_items,
560			should_record_proof,
561		);
562
563		// per-block execution overhead
564		{
565			let (stats, proof_size) = bench.bench_block()?;
566			info!(target: LOG_TARGET, "Per-block execution overhead [ns]:\n{:?}", stats);
567			let template = TemplateData::new(
568				BenchmarkType::Block,
569				&chain_name,
570				&self.params,
571				&stats,
572				proof_size,
573			)?;
574			template.write(&self.params.weight.weight_path)?;
575		}
576		// per-extrinsic execution overhead
577		{
578			let (stats, proof_size) = bench.bench_extrinsic(ext_builder)?;
579			info!(target: LOG_TARGET, "Per-extrinsic execution overhead [ns]:\n{:?}", stats);
580			let template = TemplateData::new(
581				BenchmarkType::Extrinsic,
582				&chain_name,
583				&self.params,
584				&stats,
585				proof_size,
586			)?;
587			template.write(&self.params.weight.weight_path)?;
588		}
589
590		Ok(())
591	}
592}
593
594impl BenchmarkType {
595	/// Short name of the benchmark type.
596	pub(crate) fn short_name(&self) -> &'static str {
597		match self {
598			Self::Extrinsic => "extrinsic",
599			Self::Block => "block",
600		}
601	}
602
603	/// Long name of the benchmark type.
604	pub(crate) fn long_name(&self) -> &'static str {
605		match self {
606			Self::Extrinsic => "ExtrinsicBase",
607			Self::Block => "BlockExecution",
608		}
609	}
610}
611
612#[derive(Clone, PartialEq, Debug)]
613enum ChainType {
614	Parachain(u32),
615	Relaychain,
616	Unknown,
617}
618
619impl Display for ChainType {
620	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
621		match self {
622			ChainType::Parachain(id) => write!(f, "Parachain(paraid = {})", id),
623			ChainType::Relaychain => write!(f, "Relaychain"),
624			ChainType::Unknown => write!(f, "Unknown"),
625		}
626	}
627}
628
629impl ChainType {
630	fn requires_proof_recording(&self) -> bool {
631		match self {
632			Parachain(_) => true,
633			Relaychain => false,
634			Unknown => false,
635		}
636	}
637}
638
639/// Patch the parachain id into the genesis config. This is necessary since the inherents
640/// also contain a parachain id and they need to match.
641fn patch_genesis(mut input_value: Value, para_id: Option<u32>) -> Value {
642	// If we identified a parachain we should patch a parachain id into the genesis config.
643	// This ensures compatibility with the inherents that we provide to successfully build a
644	// block.
645	if let Some(para_id) = para_id {
646		sc_chain_spec::json_patch::merge(
647			&mut input_value,
648			json!({
649				"parachainInfo": {
650					"parachainId": para_id,
651				}
652			}),
653		);
654		log::debug!(target: LOG_TARGET, "Genesis Config Json");
655		log::debug!(target: LOG_TARGET, "{}", input_value);
656	}
657	input_value
658}
659
660// Boilerplate
661impl CliConfiguration for OverheadCmd {
662	fn shared_params(&self) -> &SharedParams {
663		&self.shared_params
664	}
665
666	fn import_params(&self) -> Option<&ImportParams> {
667		Some(&self.import_params)
668	}
669
670	fn base_path(&self) -> Result<Option<BasePath>> {
671		Ok(Some(BasePath::new_temp_dir()?))
672	}
673
674	fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
675		if self.params.enable_trie_cache {
676			Ok(self.import_params().map(|x| x.trie_cache_maximum_size()).unwrap_or_default())
677		} else {
678			Ok(None)
679		}
680	}
681}
682
683#[cfg(test)]
684mod tests {
685	use crate::{
686		overhead::command::{identify_chain, ChainType, ParachainHostFunctions, DEFAULT_PARA_ID},
687		OverheadCmd,
688	};
689	use clap::Parser;
690	use codec::Decode;
691	use sc_executor::WasmExecutor;
692
693	#[test]
694	fn test_chain_type_relaychain() {
695		let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
696		let code_bytes = westend_runtime::WASM_BINARY
697			.expect("To run this test, build the wasm binary of westend-runtime")
698			.to_vec();
699		let opaque_metadata =
700			super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
701		let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
702		let chain_type = identify_chain(&metadata, None);
703		assert_eq!(chain_type, ChainType::Relaychain);
704		assert_eq!(chain_type.requires_proof_recording(), false);
705	}
706
707	#[test]
708	fn test_chain_type_parachain() {
709		let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
710		let code_bytes = cumulus_test_runtime::WASM_BINARY
711			.expect("To run this test, build the wasm binary of cumulus-test-runtime")
712			.to_vec();
713		let opaque_metadata =
714			super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
715		let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
716		let chain_type = identify_chain(&metadata, Some(100));
717		assert_eq!(chain_type, ChainType::Parachain(100));
718		assert!(chain_type.requires_proof_recording());
719		assert_eq!(identify_chain(&metadata, None), ChainType::Parachain(DEFAULT_PARA_ID));
720	}
721
722	#[test]
723	fn test_chain_type_custom() {
724		let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
725		let code_bytes = substrate_test_runtime::WASM_BINARY
726			.expect("To run this test, build the wasm binary of substrate-test-runtime")
727			.to_vec();
728		let opaque_metadata =
729			super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
730		let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
731		let chain_type = identify_chain(&metadata, None);
732		assert_eq!(chain_type, ChainType::Unknown);
733		assert_eq!(chain_type.requires_proof_recording(), false);
734	}
735
736	fn cli_succeed(args: &[&str]) -> Result<(), clap::Error> {
737		let cmd = OverheadCmd::try_parse_from(args)?;
738		assert!(cmd.check_args(&None).is_ok());
739		Ok(())
740	}
741
742	fn cli_fail(args: &[&str]) {
743		let cmd = OverheadCmd::try_parse_from(args);
744		if let Ok(cmd) = cmd {
745			assert!(cmd.check_args(&None).is_err());
746		}
747	}
748
749	#[test]
750	fn test_cli_conflicts() -> Result<(), clap::Error> {
751		// Runtime tests
752		cli_succeed(&["test", "--runtime", "path/to/runtime", "--genesis-builder", "runtime"])?;
753		cli_succeed(&["test", "--runtime", "path/to/runtime"])?;
754		cli_succeed(&[
755			"test",
756			"--runtime",
757			"path/to/runtime",
758			"--genesis-builder-preset",
759			"preset",
760		])?;
761		cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec"]);
762		cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec-genesis"]);
763		cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec-runtime"]);
764
765		// Spec tests
766		cli_succeed(&["test", "--chain", "path/to/spec"])?;
767		cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec"])?;
768		cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec-genesis"])?;
769		cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec-runtime"])?;
770		cli_fail(&["test", "--chain", "path/to/spec", "--genesis-builder", "none"]);
771		cli_fail(&["test", "--chain", "path/to/spec", "--genesis-builder", "runtime"]);
772		cli_fail(&[
773			"test",
774			"--chain",
775			"path/to/spec",
776			"--genesis-builder",
777			"runtime",
778			"--genesis-builder-preset",
779			"preset",
780		]);
781		Ok(())
782	}
783}