1use 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 cumulus_primitives_core::RelayParentOffsetApi;
42use fake_runtime_api::RuntimeApi as FakeRuntimeApi;
43use frame_support::Deserialize;
44use genesis_state::WARN_SPEC_GENESIS_CTOR;
45use log::info;
46use polkadot_parachain_primitives::primitives::Id as ParaId;
47use sc_block_builder::BlockBuilderApi;
48use sc_chain_spec::{ChainSpec, ChainSpecExtension, GenesisBlockBuilder};
49use sc_cli::{CliConfiguration, Database, ImportParams, Result, SharedParams};
50use sc_client_api::{execution_extensions::ExecutionExtensions, UsageProvider};
51use sc_client_db::{BlocksPruning, DatabaseSettings};
52use sc_executor::WasmExecutor;
53use sc_runtime_utilities::fetch_latest_metadata_from_code_blob;
54use sc_service::{new_client, new_db_backend, BasePath, ClientConfig, TFullClient, TaskManager};
55use serde::Serialize;
56use serde_json::{json, Value};
57use sp_api::{ApiExt, CallApiAt, Core, ProvideRuntimeApi};
58use sp_blockchain::HeaderBackend;
59use sp_core::H256;
60use sp_inherents::{InherentData, InherentDataProvider};
61use sp_runtime::{
62 generic,
63 traits::{BlakeTwo256, Block as BlockT},
64 DigestItem, OpaqueExtrinsic,
65};
66use sp_storage::Storage;
67use sp_wasm_interface::HostFunctions;
68use std::{
69 fmt::{Display, Formatter},
70 fs,
71 path::PathBuf,
72 sync::Arc,
73};
74use subxt::{client::RuntimeVersion, ext::futures, Metadata};
75
76const DEFAULT_PARA_ID: u32 = 100;
77const LOG_TARGET: &'static str = "polkadot_sdk_frame::benchmark::overhead";
78
79#[derive(Debug, Parser)]
81pub struct OverheadCmd {
82 #[allow(missing_docs)]
83 #[clap(flatten)]
84 pub shared_params: SharedParams,
85
86 #[allow(missing_docs)]
87 #[clap(flatten)]
88 pub import_params: ImportParams,
89
90 #[allow(missing_docs)]
91 #[clap(flatten)]
92 pub params: OverheadParams,
93}
94
95#[derive(Debug, Default, Serialize, Clone, PartialEq, Args)]
97pub struct OverheadParams {
98 #[allow(missing_docs)]
99 #[clap(flatten)]
100 pub weight: WeightParams,
101
102 #[allow(missing_docs)]
103 #[clap(flatten)]
104 pub bench: ExtrinsicBenchmarkParams,
105
106 #[allow(missing_docs)]
107 #[clap(flatten)]
108 pub hostinfo: HostInfoParams,
109
110 #[arg(long, value_name = "PATH")]
114 pub header: Option<PathBuf>,
115
116 #[arg(long)]
120 pub enable_trie_cache: bool,
121
122 #[arg(
124 long,
125 value_name = "PATH",
126 conflicts_with = "chain",
127 required_if_eq("genesis_builder", "runtime")
128 )]
129 pub runtime: Option<PathBuf>,
130
131 #[arg(long, default_value = sp_genesis_builder::DEV_RUNTIME_PRESET)]
136 pub genesis_builder_preset: String,
137
138 #[arg(long, value_enum, alias = "genesis-builder-policy")]
144 pub genesis_builder: Option<GenesisBuilderPolicy>,
145
146 #[arg(long)]
149 pub para_id: Option<u32>,
150
151 #[arg(long)]
156 pub genesis_patch: Option<PathBuf>,
157}
158
159#[derive(clap::ValueEnum, Debug, Eq, PartialEq, Clone, Copy, Serialize)]
161#[clap(rename_all = "kebab-case")]
162pub enum GenesisBuilderPolicy {
163 Runtime,
166 SpecRuntime,
168 #[value(alias = "spec")]
170 SpecGenesis,
171}
172
173#[derive(Serialize, Clone, PartialEq, Copy)]
175pub(crate) enum BenchmarkType {
176 Extrinsic,
178 Block,
180}
181
182pub type ParachainHostFunctions = (
184 cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
185 sp_io::SubstrateHostFunctions,
186);
187
188pub type BlockNumber = u32;
189
190pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
192
193pub type OpaqueBlock = generic::Block<Header, OpaqueExtrinsic>;
195
196type OverheadClient<Block, HF> = TFullClient<Block, FakeRuntimeApi, WasmExecutor<HF>>;
198
199fn create_inherent_data<Client: UsageProvider<Block> + HeaderBackend<Block>, Block: BlockT>(
206 client: &Arc<Client>,
207 chain_type: &ChainType,
208 relay_parent_offset: u32,
209) -> InherentData {
210 let genesis = client.usage_info().chain.best_hash;
211 let header = client.header(genesis).unwrap().unwrap();
212
213 let mut inherent_data = InherentData::new();
214
215 if let Parachain(para_id) = chain_type {
217 let parachain_validation_data_provider = MockValidationDataInherentDataProvider::<()> {
218 para_id: ParaId::from(*para_id),
219 current_para_block_head: Some(header.encode().into()),
220 relay_offset: 0,
221 relay_parent_offset,
222 ..Default::default()
223 };
224 let _ = futures::executor::block_on(
225 parachain_validation_data_provider.provide_inherent_data(&mut inherent_data),
226 );
227 }
228
229 let para_inherent = polkadot_primitives::InherentData {
231 bitfields: Vec::new(),
232 backed_candidates: Vec::new(),
233 disputes: Vec::new(),
234 parent_header: header,
235 };
236
237 let timestamp = sp_timestamp::InherentDataProvider::new(std::time::Duration::default().into());
239
240 let _ = futures::executor::block_on(timestamp.provide_inherent_data(&mut inherent_data));
241 let _ =
242 inherent_data.put_data(polkadot_primitives::PARACHAINS_INHERENT_IDENTIFIER, ¶_inherent);
243
244 inherent_data
245}
246
247fn identify_chain(metadata: &Metadata, para_id: Option<u32>) -> ChainType {
252 let parachain_info_exists = metadata.pallet_by_name("ParachainInfo").is_some();
253 let parachain_system_exists = metadata.pallet_by_name("ParachainSystem").is_some();
254 let para_inherent_exists = metadata.pallet_by_name("ParaInherent").is_some();
255
256 log::debug!("{} ParachainSystem", if parachain_system_exists { "✅" } else { "❌" });
257 log::debug!("{} ParachainInfo", if parachain_info_exists { "✅" } else { "❌" });
258 log::debug!("{} ParaInherent", if para_inherent_exists { "✅" } else { "❌" });
259
260 let chain_type = if parachain_system_exists && parachain_info_exists {
261 Parachain(para_id.unwrap_or(DEFAULT_PARA_ID))
262 } else if para_inherent_exists {
263 Relaychain
264 } else {
265 Unknown
266 };
267
268 log::info!(target: LOG_TARGET, "Identified Chain type from metadata: {}", chain_type);
269
270 chain_type
271}
272
273#[derive(Deserialize, Serialize, Clone, ChainSpecExtension)]
274pub struct ParachainExtension {
275 pub para_id: Option<u32>,
277}
278
279impl OverheadCmd {
280 fn state_handler_from_cli<HF: HostFunctions>(
281 &self,
282 chain_spec_from_api: Option<Box<dyn ChainSpec>>,
283 ) -> Result<(GenesisStateHandler, Option<u32>)> {
284 let genesis_builder_to_source = || match self.params.genesis_builder {
285 Some(GenesisBuilderPolicy::Runtime) | Some(GenesisBuilderPolicy::SpecRuntime) => {
286 SpecGenesisSource::Runtime(self.params.genesis_builder_preset.clone())
287 },
288 Some(GenesisBuilderPolicy::SpecGenesis) | None => {
289 log::warn!(target: LOG_TARGET, "{WARN_SPEC_GENESIS_CTOR}");
290 SpecGenesisSource::SpecJson
291 },
292 };
293
294 if let Some(chain_spec) = chain_spec_from_api {
296 log::debug!(target: LOG_TARGET, "Initializing state handler with chain-spec from API: {:?}", chain_spec);
297
298 let source = genesis_builder_to_source();
299 return Ok((GenesisStateHandler::ChainSpec(chain_spec, source), self.params.para_id));
300 };
301
302 if let Some(chain_spec_path) = &self.shared_params.chain {
304 log::debug!(target: LOG_TARGET,
305 "Initializing state handler with chain-spec from path: {:?}",
306 chain_spec_path
307 );
308 let (chain_spec, para_id_from_chain_spec) =
309 genesis_state::chain_spec_from_path::<HF>(chain_spec_path.to_string().into())?;
310
311 let source = genesis_builder_to_source();
312
313 return Ok((
314 GenesisStateHandler::ChainSpec(chain_spec, source),
315 self.params.para_id.or(para_id_from_chain_spec),
316 ));
317 };
318
319 if let Some(runtime_path) = &self.params.runtime {
322 log::debug!(target: LOG_TARGET, "Initializing state handler with runtime from path: {:?}", runtime_path);
323
324 let runtime_blob = fs::read(runtime_path)?;
325 return Ok((
326 GenesisStateHandler::Runtime(
327 runtime_blob,
328 Some(self.params.genesis_builder_preset.clone()),
329 ),
330 self.params.para_id,
331 ));
332 };
333
334 Err("Neither a runtime nor a chain-spec were specified".to_string().into())
335 }
336
337 fn check_args(
338 &self,
339 chain_spec: &Option<Box<dyn ChainSpec>>,
340 ) -> std::result::Result<(), (ErrorKind, String)> {
341 if chain_spec.is_none() &&
342 self.params.runtime.is_none() &&
343 self.shared_params.chain.is_none()
344 {
345 return Err((
346 ErrorKind::MissingRequiredArgument,
347 "Provide either a runtime via `--runtime` or a chain spec via `--chain`"
348 .to_string(),
349 ));
350 }
351
352 match self.params.genesis_builder {
353 Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::SpecRuntime) => {
354 if chain_spec.is_none() && self.shared_params.chain.is_none() {
355 return Err((
356 ErrorKind::MissingRequiredArgument,
357 "Provide a chain spec via `--chain`.".to_string(),
358 ));
359 }
360 },
361 _ => {},
362 };
363 Ok(())
364 }
365
366 pub fn run_with_default_builder_and_spec<Block, ExtraHF>(
371 &self,
372 chain_spec: Option<Box<dyn ChainSpec>>,
373 ) -> Result<()>
374 where
375 Block: BlockT<Extrinsic = OpaqueExtrinsic, Hash = H256>,
376 ExtraHF: HostFunctions,
377 {
378 self.run_with_extrinsic_builder_and_spec::<Block, ExtraHF>(
379 Box::new(|metadata, hash, version| {
380 let genesis = subxt::utils::H256::from(hash.to_fixed_bytes());
381 Box::new(SubstrateRemarkBuilder::new(metadata, genesis, version)) as Box<_>
382 }),
383 chain_spec,
384 )
385 }
386
387 pub fn run_with_extrinsic_builder_and_spec<Block, ExtraHF>(
393 &self,
394 ext_builder_provider: Box<
395 dyn FnOnce(Metadata, Block::Hash, RuntimeVersion) -> Box<dyn ExtrinsicBuilder>,
396 >,
397 chain_spec: Option<Box<dyn ChainSpec>>,
398 ) -> Result<()>
399 where
400 Block: BlockT<Extrinsic = OpaqueExtrinsic>,
401 ExtraHF: HostFunctions,
402 {
403 if let Err((error_kind, msg)) = self.check_args(&chain_spec) {
404 let mut cmd = OverheadCmd::command();
405 cmd.error(error_kind, msg).exit();
406 };
407
408 let (state_handler, para_id) =
409 self.state_handler_from_cli::<(ParachainHostFunctions, ExtraHF)>(chain_spec)?;
410
411 let user_genesis_patcher = if let Some(ref patch_path) = self.params.genesis_patch {
412 let patch_content = fs::read_to_string(patch_path)
413 .map_err(|e| format!("Failed to read genesis patch file: {}", e))?;
414
415 let patch_value: serde_json::Value = serde_json::from_str(&patch_content)
416 .map_err(|e| format!("Failed to parse genesis patch JSON: {}", e))?;
417
418 Some(patch_value)
419 } else {
420 None
421 };
422
423 let executor = WasmExecutor::<(ParachainHostFunctions, ExtraHF)>::builder()
424 .with_allow_missing_host_functions(true)
425 .build();
426
427 let opaque_metadata =
428 fetch_latest_metadata_from_code_blob(&executor, state_handler.get_code_bytes()?)
429 .map_err(|_| {
430 <&str as Into<sc_cli::Error>>::into("Unable to fetch latest stable metadata")
431 })?;
432 let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice())?;
433
434 let chain_type = identify_chain(&metadata, para_id);
436
437 let genesis_patcher = match chain_type {
440 Parachain(para_id) => Some(Box::new(move |value| {
441 let mut patched_value = patch_genesis(value, Some(para_id));
442
443 if let Some(user_patch) = &user_genesis_patcher {
444 sc_chain_spec::json_patch::merge(&mut patched_value, user_patch.clone());
445 }
446
447 patched_value
448 }) as Box<_>),
449 _ => user_genesis_patcher.map(|user_patch| {
450 Box::new(move |value| {
451 let mut patched_value = value;
452 sc_chain_spec::json_patch::merge(&mut patched_value, user_patch);
453
454 patched_value
455 }) as Box<_>
456 }),
457 };
458
459 let client = self.build_client_components::<Block, (ParachainHostFunctions, ExtraHF)>(
460 state_handler.build_storage::<(ParachainHostFunctions, ExtraHF)>(genesis_patcher)?,
461 executor,
462 &chain_type,
463 )?;
464
465 let relay_parent_offset = {
467 let genesis = client.usage_info().chain.best_hash;
468 client.runtime_api().relay_parent_offset(genesis).unwrap_or_else(|_| {
469 log::debug!(
470 target: LOG_TARGET,
471 "Runtime does not implement RelayParentOffsetApi, using default offset of 0"
472 );
473 0
474 })
475 };
476
477 let inherent_data = create_inherent_data(&client, &chain_type, relay_parent_offset);
478
479 let (ext_builder, runtime_name) = {
480 let genesis = client.usage_info().chain.best_hash;
481 let version = client.runtime_api().version(genesis).unwrap();
482 let runtime_name = version.spec_name;
483 let runtime_version = RuntimeVersion {
484 spec_version: version.spec_version,
485 transaction_version: version.transaction_version,
486 };
487
488 (ext_builder_provider(metadata, genesis, runtime_version), runtime_name)
489 };
490
491 self.run(
492 runtime_name.to_string(),
493 client,
494 inherent_data,
495 Default::default(),
496 &*ext_builder,
497 chain_type.requires_proof_recording(),
498 )
499 }
500
501 pub fn run_with_extrinsic_builder<Block, ExtraHF>(
503 &self,
504 ext_builder_provider: Box<
505 dyn FnOnce(Metadata, Block::Hash, RuntimeVersion) -> Box<dyn ExtrinsicBuilder>,
506 >,
507 ) -> Result<()>
508 where
509 Block: BlockT<Extrinsic = OpaqueExtrinsic>,
510 ExtraHF: HostFunctions,
511 {
512 self.run_with_extrinsic_builder_and_spec::<Block, ExtraHF>(ext_builder_provider, None)
513 }
514
515 fn build_client_components<Block, HF>(
516 &self,
517 genesis_storage: Storage,
518 executor: WasmExecutor<HF>,
519 chain_type: &ChainType,
520 ) -> Result<Arc<OverheadClient<Block, HF>>>
521 where
522 Block: BlockT,
523 HF: HostFunctions,
524 {
525 let extensions = ExecutionExtensions::new(None, Arc::new(executor.clone()));
526
527 let base_path = match &self.shared_params.base_path {
528 None => BasePath::new_temp_dir()?,
529 Some(path) => BasePath::from(path.clone()),
530 };
531
532 let database_source = self.database_config(
533 &base_path.path().to_path_buf(),
534 self.database_cache_size()?.unwrap_or(1024),
535 self.database()?.unwrap_or(Database::Auto),
536 )?;
537
538 let backend = new_db_backend(DatabaseSettings {
539 trie_cache_maximum_size: self.trie_cache_maximum_size()?,
540 state_pruning: None,
541 blocks_pruning: BlocksPruning::KeepAll,
542 pruning_filters: Default::default(),
543 source: database_source,
544 metrics_registry: None,
545 })?;
546
547 let genesis_block_builder = GenesisBlockBuilder::new_with_storage(
548 genesis_storage,
549 true,
550 backend.clone(),
551 executor.clone(),
552 )?;
553
554 let tokio_runtime = sc_cli::build_runtime()?;
555 let task_manager = TaskManager::new(tokio_runtime.handle().clone(), None)
556 .map_err(|_| "Unable to build task manager")?;
557
558 let client: Arc<OverheadClient<Block, HF>> = Arc::new(new_client(
559 backend.clone(),
560 executor,
561 genesis_block_builder,
562 Default::default(),
563 Default::default(),
564 extensions,
565 Box::new(task_manager.spawn_handle()),
566 None,
567 None,
568 ClientConfig {
569 offchain_worker_enabled: false,
570 offchain_indexing_api: false,
571 wasm_runtime_overrides: None,
572 no_genesis: false,
573 wasm_runtime_substitutes: Default::default(),
574 enable_import_proof_recording: chain_type.requires_proof_recording(),
575 },
576 )?);
577
578 Ok(client)
579 }
580
581 pub fn run<Block, C>(
586 &self,
587 chain_name: String,
588 client: Arc<C>,
589 inherent_data: sp_inherents::InherentData,
590 digest_items: Vec<DigestItem>,
591 ext_builder: &dyn ExtrinsicBuilder,
592 should_record_proof: bool,
593 ) -> Result<()>
594 where
595 Block: BlockT<Extrinsic = OpaqueExtrinsic>,
596 C: ProvideRuntimeApi<Block>
597 + CallApiAt<Block>
598 + UsageProvider<Block>
599 + sp_blockchain::HeaderBackend<Block>,
600 C::Api: ApiExt<Block> + BlockBuilderApi<Block>,
601 {
602 if ext_builder.pallet() != "system" || ext_builder.extrinsic() != "remark" {
603 return Err(format!("The extrinsic builder is required to build `System::Remark` extrinsics but builds `{}` extrinsics instead", ext_builder.name()).into());
604 }
605
606 let bench = Benchmark::new(
607 client,
608 self.params.bench.clone(),
609 inherent_data,
610 digest_items,
611 should_record_proof,
612 );
613
614 {
616 let (stats, proof_size) = bench.bench_block()?;
617 info!(target: LOG_TARGET, "Per-block execution overhead [ns]:\n{:?}", stats);
618 let template = TemplateData::new(
619 BenchmarkType::Block,
620 &chain_name,
621 &self.params,
622 &stats,
623 proof_size,
624 )?;
625 template.write(&self.params.weight.weight_path)?;
626 }
627 {
629 let (stats, proof_size) = bench.bench_extrinsic(ext_builder)?;
630 info!(target: LOG_TARGET, "Per-extrinsic execution overhead [ns]:\n{:?}", stats);
631 let template = TemplateData::new(
632 BenchmarkType::Extrinsic,
633 &chain_name,
634 &self.params,
635 &stats,
636 proof_size,
637 )?;
638 template.write(&self.params.weight.weight_path)?;
639 }
640
641 Ok(())
642 }
643}
644
645impl BenchmarkType {
646 pub(crate) fn short_name(&self) -> &'static str {
648 match self {
649 Self::Extrinsic => "extrinsic",
650 Self::Block => "block",
651 }
652 }
653
654 pub(crate) fn long_name(&self) -> &'static str {
656 match self {
657 Self::Extrinsic => "ExtrinsicBase",
658 Self::Block => "BlockExecution",
659 }
660 }
661}
662
663#[derive(Clone, PartialEq, Debug)]
664enum ChainType {
665 Parachain(u32),
666 Relaychain,
667 Unknown,
668}
669
670impl Display for ChainType {
671 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
672 match self {
673 ChainType::Parachain(id) => write!(f, "Parachain(paraid = {})", id),
674 ChainType::Relaychain => write!(f, "Relaychain"),
675 ChainType::Unknown => write!(f, "Unknown"),
676 }
677 }
678}
679
680impl ChainType {
681 fn requires_proof_recording(&self) -> bool {
682 match self {
683 Parachain(_) => true,
684 Relaychain => false,
685 Unknown => false,
686 }
687 }
688}
689
690fn patch_genesis(mut input_value: Value, para_id: Option<u32>) -> Value {
693 if let Some(para_id) = para_id {
697 sc_chain_spec::json_patch::merge(
698 &mut input_value,
699 json!({
700 "parachainInfo": {
701 "parachainId": para_id,
702 }
703 }),
704 );
705 log::debug!(target: LOG_TARGET, "Genesis Config Json");
706 log::debug!(target: LOG_TARGET, "{}", input_value);
707 }
708 input_value
709}
710
711impl CliConfiguration for OverheadCmd {
713 fn shared_params(&self) -> &SharedParams {
714 &self.shared_params
715 }
716
717 fn import_params(&self) -> Option<&ImportParams> {
718 Some(&self.import_params)
719 }
720
721 fn base_path(&self) -> Result<Option<BasePath>> {
722 Ok(Some(BasePath::new_temp_dir()?))
723 }
724
725 fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
726 if self.params.enable_trie_cache {
727 Ok(self.import_params().map(|x| x.trie_cache_maximum_size()).unwrap_or_default())
728 } else {
729 Ok(None)
730 }
731 }
732}
733
734#[cfg(test)]
735mod tests {
736 use crate::{
737 overhead::command::{identify_chain, ChainType, ParachainHostFunctions, DEFAULT_PARA_ID},
738 OverheadCmd,
739 };
740 use clap::Parser;
741 use codec::Decode;
742 use sc_executor::WasmExecutor;
743
744 #[test]
745 fn test_chain_type_relaychain() {
746 let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
747 let code_bytes = westend_runtime::WASM_BINARY
748 .expect("To run this test, build the wasm binary of westend-runtime")
749 .to_vec();
750 let opaque_metadata =
751 super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
752 let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
753 let chain_type = identify_chain(&metadata, None);
754 assert_eq!(chain_type, ChainType::Relaychain);
755 assert_eq!(chain_type.requires_proof_recording(), false);
756 }
757
758 #[test]
759 fn test_chain_type_parachain() {
760 let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
761 let code_bytes = cumulus_test_runtime::WASM_BINARY
762 .expect("To run this test, build the wasm binary of cumulus-test-runtime")
763 .to_vec();
764 let opaque_metadata =
765 super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
766 let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
767 let chain_type = identify_chain(&metadata, Some(100));
768 assert_eq!(chain_type, ChainType::Parachain(100));
769 assert!(chain_type.requires_proof_recording());
770 assert_eq!(identify_chain(&metadata, None), ChainType::Parachain(DEFAULT_PARA_ID));
771 }
772
773 #[test]
774 fn test_chain_type_custom() {
775 let executor: WasmExecutor<ParachainHostFunctions> = WasmExecutor::builder().build();
776 let code_bytes = substrate_test_runtime::WASM_BINARY
777 .expect("To run this test, build the wasm binary of substrate-test-runtime")
778 .to_vec();
779 let opaque_metadata =
780 super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
781 let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
782 let chain_type = identify_chain(&metadata, None);
783 assert_eq!(chain_type, ChainType::Unknown);
784 assert_eq!(chain_type.requires_proof_recording(), false);
785 }
786
787 fn cli_succeed(args: &[&str]) -> Result<(), clap::Error> {
788 let cmd = OverheadCmd::try_parse_from(args)?;
789 assert!(cmd.check_args(&None).is_ok());
790 Ok(())
791 }
792
793 fn cli_fail(args: &[&str]) {
794 let cmd = OverheadCmd::try_parse_from(args);
795 if let Ok(cmd) = cmd {
796 assert!(cmd.check_args(&None).is_err());
797 }
798 }
799
800 #[test]
801 fn test_cli_conflicts() -> Result<(), clap::Error> {
802 cli_succeed(&["test", "--runtime", "path/to/runtime", "--genesis-builder", "runtime"])?;
804 cli_succeed(&["test", "--runtime", "path/to/runtime"])?;
805 cli_succeed(&[
806 "test",
807 "--runtime",
808 "path/to/runtime",
809 "--genesis-builder-preset",
810 "preset",
811 ])?;
812
813 cli_succeed(&[
815 "test",
816 "--runtime",
817 "path/to/runtime",
818 "--genesis-patch",
819 "path/to/patch.json",
820 ])?;
821 cli_succeed(&[
822 "test",
823 "--runtime",
824 "path/to/runtime",
825 "--genesis-builder",
826 "runtime",
827 "--genesis-patch",
828 "path/to/patch.json",
829 ])?;
830 cli_succeed(&[
831 "test",
832 "--runtime",
833 "path/to/runtime",
834 "--genesis-builder-preset",
835 "preset",
836 "--genesis-patch",
837 "path/to/patch.json",
838 ])?;
839
840 cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec"]);
841 cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec-genesis"]);
842 cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec-runtime"]);
843
844 cli_succeed(&["test", "--chain", "path/to/spec"])?;
846 cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec"])?;
847 cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec-genesis"])?;
848 cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec-runtime"])?;
849
850 cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-patch", "path/to/patch.json"])?;
852 cli_succeed(&[
853 "test",
854 "--chain",
855 "path/to/spec",
856 "--genesis-builder",
857 "spec",
858 "--genesis-patch",
859 "path/to/patch.json",
860 ])?;
861 cli_succeed(&[
862 "test",
863 "--chain",
864 "path/to/spec",
865 "--genesis-builder",
866 "spec-genesis",
867 "--genesis-patch",
868 "path/to/patch.json",
869 ])?;
870 cli_succeed(&[
871 "test",
872 "--chain",
873 "path/to/spec",
874 "--genesis-builder",
875 "spec-runtime",
876 "--genesis-patch",
877 "path/to/patch.json",
878 ])?;
879
880 cli_fail(&["test", "--chain", "path/to/spec", "--genesis-builder", "none"]);
881 cli_fail(&["test", "--chain", "path/to/spec", "--genesis-builder", "runtime"]);
882 cli_fail(&[
883 "test",
884 "--chain",
885 "path/to/spec",
886 "--genesis-builder",
887 "runtime",
888 "--genesis-builder-preset",
889 "preset",
890 ]);
891 Ok(())
892 }
893}