diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml
index 639b8b3d4dcf..890cf5199169 100644
--- a/cumulus/polkadot-parachain/Cargo.toml
+++ b/cumulus/polkadot-parachain/Cargo.toml
@@ -18,6 +18,7 @@ path = "src/main.rs"
async-trait = "0.1.79"
clap = { version = "4.5.3", features = ["derive"] }
codec = { package = "parity-scale-codec", version = "3.6.12" }
+color-print = "0.3.4"
futures = "0.3.28"
hex-literal = "0.4.1"
log = { workspace = true, default-features = true }
@@ -111,7 +112,6 @@ cumulus-client-service = { path = "../client/service" }
cumulus-primitives-aura = { path = "../primitives/aura" }
cumulus-primitives-core = { path = "../primitives/core" }
cumulus-relay-chain-interface = { path = "../client/relay-chain-interface" }
-color-print = "0.3.4"
[build-dependencies]
substrate-build-script-utils = { path = "../../substrate/utils/build-script-utils" }
diff --git a/cumulus/polkadot-parachain/src/cli.rs b/cumulus/polkadot-parachain/src/cli.rs
index f7d2fd0f0be3..fa4b4da1ba9c 100644
--- a/cumulus/polkadot-parachain/src/cli.rs
+++ b/cumulus/polkadot-parachain/src/cli.rs
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see .
+use clap::{CommandFactory, FromArgMatches};
use std::path::PathBuf;
/// Sub-commands supported by the collator.
@@ -108,18 +109,19 @@ pub struct RelayChainCli {
}
impl RelayChainCli {
- /// Parse the relay chain CLI parameters using the para chain `Configuration`.
+ /// Parse the relay chain CLI parameters using the parachain `Configuration`.
pub fn new<'a>(
para_config: &sc_service::Configuration,
relay_chain_args: impl Iterator- ,
) -> Self {
+ let polkadot_cmd = polkadot_cli::RunCmd::command().no_binary_name(true);
+ let matches = polkadot_cmd.get_matches_from(relay_chain_args);
+ let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit());
+
let extension = crate::chain_spec::Extensions::try_get(&*para_config.chain_spec);
let chain_id = extension.map(|e| e.relay_chain.clone());
+
let base_path = para_config.base_path.path().join("polkadot");
- Self {
- base_path: Some(base_path),
- chain_id,
- base: clap::Parser::parse_from(relay_chain_args),
- }
+ Self { base, chain_id, base_path: Some(base_path) }
}
}
diff --git a/cumulus/polkadot-parachain/src/command.rs b/cumulus/polkadot-parachain/src/command.rs
index 653ea3281f0f..6b3f4b4cd0a7 100644
--- a/cumulus/polkadot-parachain/src/command.rs
+++ b/cumulus/polkadot-parachain/src/command.rs
@@ -530,13 +530,9 @@ pub fn run() -> Result<()> {
}),
Some(Subcommand::PurgeChain(cmd)) => {
let runner = cli.create_runner(cmd)?;
+ let polkadot_cli = RelayChainCli::new(runner.config(), cli.relaychain_args.iter());
runner.sync_run(|config| {
- let polkadot_cli = RelayChainCli::new(
- &config,
- [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args.iter()),
- );
-
let polkadot_config = SubstrateCli::create_configuration(
&polkadot_cli,
&polkadot_cli,
@@ -603,6 +599,7 @@ pub fn run() -> Result<()> {
Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
None => {
let runner = cli.create_runner(&cli.run.normalize())?;
+ let polkadot_cli = RelayChainCli::new(runner.config(), cli.relaychain_args.iter());
let collator_options = cli.run.collator_options();
runner.run_node_until_exit(|config| async move {
@@ -648,11 +645,6 @@ pub fn run() -> Result<()> {
.map(|e| e.para_id)
.ok_or("Could not find parachain extension in chain-spec.")?;
- let polkadot_cli = RelayChainCli::new(
- &config,
- [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args.iter()),
- );
-
let id = ParaId::from(para_id);
let parachain_account =
@@ -667,7 +659,7 @@ pub fn run() -> Result<()> {
info!("Parachain Account: {}", parachain_account);
info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" });
- match polkadot_config.network.network_backend {
+ match config.network.network_backend {
sc_network::config::NetworkBackendType::Libp2p =>
start_node::>(
config,
diff --git a/cumulus/polkadot-parachain/src/common/aura.rs b/cumulus/polkadot-parachain/src/common/aura.rs
new file mode 100644
index 000000000000..9f72d847926f
--- /dev/null
+++ b/cumulus/polkadot-parachain/src/common/aura.rs
@@ -0,0 +1,68 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus. If not, see .
+
+//! Aura-related primitives for cumulus parachain collators.
+
+use codec::Codec;
+use cumulus_primitives_aura::AuraUnincludedSegmentApi;
+use cumulus_primitives_core::BlockT;
+use sp_consensus_aura::AuraApi;
+use sp_runtime::app_crypto::{AppCrypto, AppPair, AppSignature, Pair};
+
+/// Convenience trait for defining the basic bounds of an `AuraId`.
+pub trait AuraIdT: AppCrypto + Codec + Send {
+ /// Extra bounds for the `Pair`.
+ type BoundedPair: AppPair + AppCrypto;
+
+ /// Extra bounds for the `Signature`.
+ type BoundedSignature: AppSignature
+ + TryFrom>
+ + std::hash::Hash
+ + sp_runtime::traits::Member
+ + Codec;
+}
+
+impl AuraIdT for T
+where
+ T: AppCrypto + Codec + Send + Sync,
+ <::Pair as AppCrypto>::Signature:
+ TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec,
+{
+ type BoundedPair = ::Pair;
+ type BoundedSignature = <::Pair as AppCrypto>::Signature;
+}
+
+/// Convenience trait for defining the basic bounds of a parachain runtime that supports
+/// the Aura consensus.
+pub trait AuraRuntimeApi:
+ sp_api::ApiExt
+ + AuraApi::Public>
+ + AuraUnincludedSegmentApi
+ + Sized
+{
+ /// Check if the runtime has the Aura API.
+ fn has_aura_api(&self, at: Block::Hash) -> bool {
+ self.has_api::::Public>>(at)
+ .unwrap_or(false)
+ }
+}
+
+impl AuraRuntimeApi for T where
+ T: sp_api::ApiExt
+ + AuraApi::Public>
+ + AuraUnincludedSegmentApi
+{
+}
diff --git a/cumulus/polkadot-parachain/src/common/mod.rs b/cumulus/polkadot-parachain/src/common/mod.rs
new file mode 100644
index 000000000000..5adbb4137cd3
--- /dev/null
+++ b/cumulus/polkadot-parachain/src/common/mod.rs
@@ -0,0 +1,67 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus. If not, see .
+
+//! Cumulus parachain collator primitives.
+
+#![warn(missing_docs)]
+
+pub mod aura;
+
+use cumulus_primitives_core::CollectCollationInfo;
+use sp_api::{ApiExt, CallApiAt, ConstructRuntimeApi, Metadata};
+use sp_block_builder::BlockBuilder;
+use sp_runtime::traits::Block as BlockT;
+use sp_session::SessionKeys;
+use sp_transaction_pool::runtime_api::TaggedTransactionQueue;
+
+/// Convenience trait that defines the basic bounds for the `RuntimeApi` of a parachain node.
+pub trait NodeRuntimeApi:
+ ApiExt
+ + Metadata
+ + SessionKeys
+ + BlockBuilder
+ + TaggedTransactionQueue
+ + CollectCollationInfo
+ + Sized
+{
+}
+
+impl NodeRuntimeApi for T where
+ T: ApiExt
+ + Metadata
+ + SessionKeys
+ + BlockBuilder
+ + TaggedTransactionQueue
+ + CollectCollationInfo
+{
+}
+
+/// Convenience trait that defines the basic bounds for the `ConstructRuntimeApi` of a parachain
+/// node.
+pub trait ConstructNodeRuntimeApi>:
+ ConstructRuntimeApi + Send + Sync + 'static
+{
+ /// Basic bounds for the `RuntimeApi` of a parachain node.
+ type BoundedRuntimeApi: NodeRuntimeApi;
+}
+
+impl> ConstructNodeRuntimeApi for T
+where
+ T: ConstructRuntimeApi + Send + Sync + 'static,
+ T::RuntimeApi: NodeRuntimeApi,
+{
+ type BoundedRuntimeApi = T::RuntimeApi;
+}
diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs
index 82c02943c5fc..0b79d338c168 100644
--- a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs
+++ b/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs
@@ -105,12 +105,6 @@ sp_api::impl_runtime_apis! {
}
}
- impl sp_offchain::OffchainWorkerApi for Runtime {
- fn offchain_worker(_: &::Header) {
- unimplemented!()
- }
- }
-
impl sp_session::SessionKeys for Runtime {
fn generate_session_keys(_: Option>) -> Vec {
unimplemented!()
diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs
index 6b718e912164..823eb9ab584a 100644
--- a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs
+++ b/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs
@@ -105,12 +105,6 @@ sp_api::impl_runtime_apis! {
}
}
- impl sp_offchain::OffchainWorkerApi for Runtime {
- fn offchain_worker(_: &::Header) {
- unimplemented!()
- }
- }
-
impl sp_session::SessionKeys for Runtime {
fn generate_session_keys(_: Option>) -> Vec {
unimplemented!()
diff --git a/cumulus/polkadot-parachain/src/main.rs b/cumulus/polkadot-parachain/src/main.rs
index 0757bea84aae..2bf659228bc6 100644
--- a/cumulus/polkadot-parachain/src/main.rs
+++ b/cumulus/polkadot-parachain/src/main.rs
@@ -22,6 +22,7 @@
mod chain_spec;
mod cli;
mod command;
+mod common;
mod fake_runtime_api;
mod rpc;
mod service;
diff --git a/cumulus/polkadot-parachain/src/service.rs b/cumulus/polkadot-parachain/src/service.rs
index 19ad75e384ce..9cd3a0037223 100644
--- a/cumulus/polkadot-parachain/src/service.rs
+++ b/cumulus/polkadot-parachain/src/service.rs
@@ -14,13 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see .
-use codec::{Codec, Decode};
+use codec::Decode;
use cumulus_client_cli::CollatorOptions;
use cumulus_client_collator::service::CollatorService;
use cumulus_client_consensus_aura::collators::lookahead::{self as aura, Params as AuraParams};
-use cumulus_client_consensus_common::{
- ParachainBlockImport as TParachainBlockImport, ParachainCandidate, ParachainConsensus,
-};
+use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport;
use cumulus_client_consensus_proposer::Proposer;
#[allow(deprecated)]
use cumulus_client_service::old_consensus;
@@ -28,22 +26,26 @@ use cumulus_client_service::{
build_network, build_relay_chain_interface, prepare_node_config, start_relay_chain_tasks,
BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams,
};
-use cumulus_primitives_core::{
- relay_chain::{Hash as PHash, PersistedValidationData, ValidationCode},
- ParaId,
-};
+use cumulus_primitives_core::{relay_chain::ValidationCode, ParaId};
use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface};
use sc_rpc::DenyUnsafe;
-use sp_core::Pair;
use jsonrpsee::RpcModule;
-use crate::{fake_runtime_api::aura::RuntimeApi as FakeRuntimeApi, rpc};
-pub use parachains_common::{AccountId, AuraId, Balance, Block, Hash, Header, Nonce};
+use crate::{
+ common::{
+ aura::{AuraIdT, AuraRuntimeApi},
+ ConstructNodeRuntimeApi,
+ },
+ fake_runtime_api::aura::RuntimeApi as FakeRuntimeApi,
+ rpc,
+};
+pub use parachains_common::{AccountId, AuraId, Balance, Block, Hash, Nonce};
use cumulus_client_consensus_relay_chain::Verifier as RelayChainVerifier;
-use futures::{lock::Mutex, prelude::*};
+use futures::prelude::*;
use prometheus_endpoint::Registry;
+use sc_client_api::Backend as ClientApiBackend;
use sc_consensus::{
import_queue::{BasicQueue, Verifier as VerifierT},
BlockImportParams, ImportQueue,
@@ -53,8 +55,8 @@ use sc_network::{config::FullNetworkConfiguration, service::traits::NetworkBacke
use sc_network_sync::SyncingService;
use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager};
use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle};
-use sp_api::{ApiExt, ConstructRuntimeApi, ProvideRuntimeApi};
-use sp_consensus_aura::AuraApi;
+use sp_api::{ConstructRuntimeApi, ProvideRuntimeApi};
+use sp_blockchain::HeaderBackend;
use sp_core::traits::SpawnEssentialNamed;
use sp_keystore::KeystorePtr;
use sp_runtime::{
@@ -100,13 +102,7 @@ pub fn new_partial(
build_import_queue: BIQ,
) -> Result, sc_service::Error>
where
- RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static,
- RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue
- + sp_api::Metadata
- + sp_session::SessionKeys
- + sp_api::ApiExt
- + sp_offchain::OffchainWorkerApi
- + sp_block_builder::BlockBuilder,
+ RuntimeApi: ConstructNodeRuntimeApi>,
BIQ: FnOnce(
Arc>,
ParachainBlockImport,
@@ -200,16 +196,7 @@ async fn start_node_impl(
hwbench: Option,
) -> sc_service::error::Result<(TaskManager, Arc>)>
where
- RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static,
- RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue
- + sp_api::Metadata
- + sp_session::SessionKeys
- + sp_api::ApiExt
- + sp_offchain::OffchainWorkerApi
- + sp_block_builder::BlockBuilder
- + cumulus_primitives_core::CollectCollationInfo
- + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi
- + substrate_frame_rpc_system::AccountNonceApi,
+ RuntimeApi: ConstructNodeRuntimeApi>,
RB: Fn(
DenyUnsafe,
Arc>,
@@ -529,61 +516,6 @@ impl BuildOnAccess {
}
}
-/// Special [`ParachainConsensus`] implementation that waits for the upgrade from
-/// shell to a parachain runtime that implements Aura.
-struct WaitForAuraConsensus {
- client: Arc,
- aura_consensus: Arc>>>>,
- relay_chain_consensus: Arc>>>,
- _phantom: PhantomData,
-}
-
-impl Clone for WaitForAuraConsensus {
- fn clone(&self) -> Self {
- Self {
- client: self.client.clone(),
- aura_consensus: self.aura_consensus.clone(),
- relay_chain_consensus: self.relay_chain_consensus.clone(),
- _phantom: PhantomData,
- }
- }
-}
-
-#[async_trait::async_trait]
-impl ParachainConsensus for WaitForAuraConsensus
-where
- Client: sp_api::ProvideRuntimeApi + Send + Sync,
- Client::Api: AuraApi,
- AuraId: Send + Codec + Sync,
-{
- async fn produce_candidate(
- &mut self,
- parent: &Header,
- relay_parent: PHash,
- validation_data: &PersistedValidationData,
- ) -> Option> {
- if self
- .client
- .runtime_api()
- .has_api::>(parent.hash())
- .unwrap_or(false)
- {
- self.aura_consensus
- .lock()
- .await
- .get_mut()
- .produce_candidate(parent, relay_parent, validation_data)
- .await
- } else {
- self.relay_chain_consensus
- .lock()
- .await
- .produce_candidate(parent, relay_parent, validation_data)
- .await
- }
- }
-}
-
struct Verifier {
client: Arc,
aura_verifier: BuildOnAccess>>,
@@ -592,22 +524,16 @@ struct Verifier {
}
#[async_trait::async_trait]
-impl VerifierT for Verifier
+impl VerifierT for Verifier
where
Client: sp_api::ProvideRuntimeApi + Send + Sync,
- Client::Api: AuraApi,
- AuraId: Send + Sync + Codec,
+ Client::Api: AuraRuntimeApi,
{
async fn verify(
&mut self,
block_import: BlockImportParams,
) -> Result, String> {
- if self
- .client
- .runtime_api()
- .has_api::>(*block_import.header.parent_hash())
- .unwrap_or(false)
- {
+ if self.client.runtime_api().has_aura_api(*block_import.header.parent_hash()) {
self.aura_verifier.get_mut().verify(block_import).await
} else {
self.relay_chain_verifier.verify(block_import).await
@@ -617,7 +543,7 @@ where
/// Build the import queue for parachain runtimes that started with relay chain consensus and
/// switched to aura.
-pub fn build_relay_to_aura_import_queue(
+pub fn build_relay_to_aura_import_queue(
client: Arc>,
block_import: ParachainBlockImport,
config: &Configuration,
@@ -625,16 +551,8 @@ pub fn build_relay_to_aura_import_queue(
task_manager: &TaskManager,
) -> Result, sc_service::Error>
where
- RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static,
- RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue
- + sp_api::Metadata
- + sp_session::SessionKeys
- + sp_api::ApiExt
- + sp_offchain::OffchainWorkerApi
- + sp_block_builder::BlockBuilder
- + sp_consensus_aura::AuraApi::Pair as Pair>::Public>,
- <::Pair as Pair>::Signature:
- TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec,
+ RuntimeApi: ConstructNodeRuntimeApi>,
+ RuntimeApi::RuntimeApi: AuraRuntimeApi,
{
let verifier_client = client.clone();
@@ -714,11 +632,7 @@ pub async fn start_generic_aura_lookahead_node>
///
/// Uses the lookahead collator to support async backing.
#[sc_tracing::logging::prefix_logs_with("Parachain")]
-pub async fn start_asset_hub_lookahead_node<
- RuntimeApi,
- AuraId: AppCrypto + Send + Codec + Sync,
- Net,
->(
+pub async fn start_asset_hub_lookahead_node(
parachain_config: Configuration,
polkadot_config: Configuration,
collator_options: CollatorOptions,
@@ -726,20 +640,10 @@ pub async fn start_asset_hub_lookahead_node<
hwbench: Option,
) -> sc_service::error::Result<(TaskManager, Arc>)>
where
- RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static,
- RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue
- + sp_api::Metadata
- + sp_session::SessionKeys
- + sp_api::ApiExt
- + sp_offchain::OffchainWorkerApi
- + sp_block_builder::BlockBuilder
- + cumulus_primitives_core::CollectCollationInfo
- + sp_consensus_aura::AuraApi::Pair as Pair>::Public>
+ RuntimeApi: ConstructNodeRuntimeApi>,
+ RuntimeApi::RuntimeApi: AuraRuntimeApi
+ pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi
- + substrate_frame_rpc_system::AccountNonceApi
- + cumulus_primitives_aura::AuraUnincludedSegmentApi,
- <::Pair as Pair>::Signature:
- TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec,
+ + substrate_frame_rpc_system::AccountNonceApi,
Net: NetworkBackend,
{
start_node_impl::(
@@ -807,11 +711,7 @@ where
// Check if we have upgraded to an Aura compatible runtime and transition if
// necessary.
- if client
- .runtime_api()
- .has_api::>(last_head_hash)
- .unwrap_or(false)
- {
+ if client.runtime_api().has_aura_api(last_head_hash) {
// Respond to this request before transitioning to Aura.
request.complete(None);
break
@@ -930,14 +830,14 @@ fn start_relay_chain_consensus(
}
/// Start consensus using the lookahead aura collator.
-fn start_lookahead_aura_consensus(
- client: Arc>,
- block_import: ParachainBlockImport,
+fn start_lookahead_aura_consensus(
+ client: Arc>,
+ block_import: ParachainBlockImport,
prometheus_registry: Option<&Registry>,
telemetry: Option,
task_manager: &TaskManager,
relay_chain_interface: Arc,
- transaction_pool: Arc>>,
+ transaction_pool: Arc>>,
sync_oracle: Arc>,
keystore: KeystorePtr,
relay_chain_slot_duration: Duration,
@@ -946,7 +846,16 @@ fn start_lookahead_aura_consensus(
overseer_handle: OverseerHandle,
announce_block: Arc>) + Send + Sync>,
backend: Arc,
-) -> Result<(), sc_service::Error> {
+) -> Result<(), sc_service::Error>
+where
+ RuntimeApi: ConstructNodeRuntimeApi>,
+ RuntimeApi::RuntimeApi: AuraRuntimeApi,
+{
+ let info = backend.blockchain().info();
+ if !client.runtime_api().has_aura_api(info.finalized_hash) {
+ return Err(sc_service::error::Error::Other("Missing aura runtime APIs".to_string()));
+ }
+
let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording(
task_manager.spawn_handle(),
client.clone(),
diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs
index 187e18aa3cac..e4788f1f3376 100644
--- a/substrate/client/service/src/config.rs
+++ b/substrate/client/service/src/config.rs
@@ -280,7 +280,7 @@ impl Default for RpcMethods {
static mut BASE_PATH_TEMP: Option = None;
/// The base path that is used for everything that needs to be written on disk to run a node.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
pub struct BasePath {
path: PathBuf,
}