diff --git a/Cargo.lock b/Cargo.lock index 9d2d6caa62296..83c98c65c6cb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5829,7 +5829,6 @@ dependencies = [ "atty", "chrono", "derive_more", - "directories", "env_logger 0.7.1", "fdlimit", "futures 0.3.4", @@ -6607,6 +6606,7 @@ name = "sc-service" version = "0.8.0-rc3" dependencies = [ "derive_more", + "directories", "exit-future", "futures 0.1.29", "futures 0.3.4", @@ -6662,6 +6662,7 @@ dependencies = [ "substrate-prometheus-endpoint", "substrate-test-runtime-client", "sysinfo", + "tempfile", "tracing", "wasm-timer", ] diff --git a/bin/node/cli/src/cli.rs b/bin/node/cli/src/cli.rs index 0156faf47ee4a..29e916fe0180e 100644 --- a/bin/node/cli/src/cli.rs +++ b/bin/node/cli/src/cli.rs @@ -20,7 +20,7 @@ use sc_cli::RunCmd; use structopt::StructOpt; /// An overarching CLI command definition. -#[derive(Clone, Debug, StructOpt)] +#[derive(Debug, StructOpt)] pub struct Cli { /// Possible subcommand with parameters. #[structopt(subcommand)] @@ -31,7 +31,7 @@ pub struct Cli { } /// Possible subcommands of the main binary. -#[derive(Clone, Debug, StructOpt)] +#[derive(Debug, StructOpt)] pub enum Subcommand { /// A set of base subcommands handled by `sc_cli`. #[structopt(flatten)] diff --git a/bin/node/cli/tests/temp_base_path_works.rs b/bin/node/cli/tests/temp_base_path_works.rs new file mode 100644 index 0000000000000..9351568d87955 --- /dev/null +++ b/bin/node/cli/tests/temp_base_path_works.rs @@ -0,0 +1,72 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +#![cfg(unix)] + +use assert_cmd::cargo::cargo_bin; +use nix::sys::signal::{kill, Signal::SIGINT}; +use nix::unistd::Pid; +use regex::Regex; +use std::convert::TryInto; +use std::io::Read; +use std::path::PathBuf; +use std::process::{Command, Stdio}; +use std::thread; +use std::time::Duration; + +pub mod common; + +#[test] +fn temp_base_path_works() { + let mut cmd = Command::new(cargo_bin("substrate")); + + let mut cmd = cmd + .args(&["--dev", "--tmp"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + // Let it produce some blocks. + thread::sleep(Duration::from_secs(30)); + assert!( + cmd.try_wait().unwrap().is_none(), + "the process should still be running" + ); + + // Stop the process + kill(Pid::from_raw(cmd.id().try_into().unwrap()), SIGINT).unwrap(); + assert!(common::wait_for(&mut cmd, 40) + .map(|x| x.success()) + .unwrap_or_default()); + + // Ensure the database has been deleted + let mut stderr = String::new(); + cmd.stderr.unwrap().read_to_string(&mut stderr).unwrap(); + let re = Regex::new(r"Database: .+ at (\S+)").unwrap(); + let db_path = PathBuf::from( + re.captures(stderr.as_str()) + .unwrap() + .get(1) + .unwrap() + .as_str() + .to_string(), + ); + + assert!(!db_path.exists()); +} diff --git a/bin/node/inspect/src/cli.rs b/bin/node/inspect/src/cli.rs index 4475d31755fdc..d66644bab52fa 100644 --- a/bin/node/inspect/src/cli.rs +++ b/bin/node/inspect/src/cli.rs @@ -23,7 +23,7 @@ use sc_cli::{ImportParams, SharedParams}; use structopt::StructOpt; /// The `inspect` command used to print decoded chain data. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct InspectCmd { #[allow(missing_docs)] #[structopt(flatten)] @@ -39,7 +39,7 @@ pub struct InspectCmd { } /// A possible inspect sub-commands. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub enum InspectSubCmd { /// Decode block with native version of runtime and print out the details. Block { diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 4bdacfcbd2b50..7ffc27749b136 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -20,7 +20,6 @@ regex = "1.3.1" time = "0.1.42" ansi_term = "0.12.1" lazy_static = "1.4.0" -directories = "2.0.2" tokio = { version = "0.2.9", features = [ "signal", "rt-core", "rt-threaded" ] } futures = "0.3.4" fdlimit = "0.1.4" diff --git a/client/cli/src/commands/build_spec_cmd.rs b/client/cli/src/commands/build_spec_cmd.rs index d2e2ef3a5467c..23626359ff131 100644 --- a/client/cli/src/commands/build_spec_cmd.rs +++ b/client/cli/src/commands/build_spec_cmd.rs @@ -27,7 +27,7 @@ use structopt::StructOpt; use std::io::Write; /// The `build-spec` command used to build a specification. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct BuildSpecCmd { /// Force raw genesis storage output. #[structopt(long = "raw")] diff --git a/client/cli/src/commands/check_block_cmd.rs b/client/cli/src/commands/check_block_cmd.rs index d1241f010d597..c000ea7fb11ee 100644 --- a/client/cli/src/commands/check_block_cmd.rs +++ b/client/cli/src/commands/check_block_cmd.rs @@ -25,7 +25,7 @@ use std::{fmt::Debug, str::FromStr}; use structopt::StructOpt; /// The `check-block` command used to validate blocks. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct CheckBlockCmd { /// Block hash or number #[structopt(value_name = "HASH or NUMBER")] diff --git a/client/cli/src/commands/export_blocks_cmd.rs b/client/cli/src/commands/export_blocks_cmd.rs index 2fdc408250bce..7c523c0555d55 100644 --- a/client/cli/src/commands/export_blocks_cmd.rs +++ b/client/cli/src/commands/export_blocks_cmd.rs @@ -31,7 +31,7 @@ use std::path::PathBuf; use structopt::StructOpt; /// The `export-blocks` command used to export blocks. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ExportBlocksCmd { /// Output file name or stdout if unspecified. #[structopt(parse(from_os_str))] diff --git a/client/cli/src/commands/export_state_cmd.rs b/client/cli/src/commands/export_state_cmd.rs index 33111e7737b1c..23a43a178abe5 100644 --- a/client/cli/src/commands/export_state_cmd.rs +++ b/client/cli/src/commands/export_state_cmd.rs @@ -27,7 +27,7 @@ use structopt::StructOpt; /// The `export-state` command used to export the state of a given block into /// a chain spec. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ExportStateCmd { /// Block hash or number. #[structopt(value_name = "HASH or NUMBER")] @@ -59,7 +59,7 @@ impl ExportStateCmd { { info!("Exporting raw state..."); let mut input_spec = config.chain_spec.cloned_box(); - let block_id = self.input.clone().map(|b| b.parse()).transpose()?; + let block_id = self.input.as_ref().map(|b| b.parse()).transpose()?; let raw_state = builder(config)?.export_raw_state(block_id)?; input_spec.set_storage(raw_state); diff --git a/client/cli/src/commands/import_blocks_cmd.rs b/client/cli/src/commands/import_blocks_cmd.rs index a74f4d524c95b..8e178c4b97964 100644 --- a/client/cli/src/commands/import_blocks_cmd.rs +++ b/client/cli/src/commands/import_blocks_cmd.rs @@ -29,7 +29,7 @@ use std::path::PathBuf; use structopt::StructOpt; /// The `import-blocks` command used to import blocks. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ImportBlocksCmd { /// Input file or stdin if unspecified. #[structopt(parse(from_os_str))] diff --git a/client/cli/src/commands/mod.rs b/client/cli/src/commands/mod.rs index 62757890ef01d..a4d5c8ca7f20b 100644 --- a/client/cli/src/commands/mod.rs +++ b/client/cli/src/commands/mod.rs @@ -27,11 +27,11 @@ mod run_cmd; pub use self::build_spec_cmd::BuildSpecCmd; pub use self::check_block_cmd::CheckBlockCmd; pub use self::export_blocks_cmd::ExportBlocksCmd; +pub use self::export_state_cmd::ExportStateCmd; pub use self::import_blocks_cmd::ImportBlocksCmd; pub use self::purge_chain_cmd::PurgeChainCmd; pub use self::revert_cmd::RevertCmd; pub use self::run_cmd::RunCmd; -pub use self::export_state_cmd::ExportStateCmd; use std::fmt::Debug; use structopt::StructOpt; @@ -40,7 +40,7 @@ use structopt::StructOpt; /// The core commands are split into multiple subcommands and `Run` is the default subcommand. From /// the CLI user perspective, it is not visible that `Run` is a subcommand. So, all parameters of /// `Run` are exported as main executable parameters. -#[derive(Debug, Clone, StructOpt)] +#[derive(Debug, StructOpt)] pub enum Subcommand { /// Build a spec.json file, outputs to stdout. BuildSpec(BuildSpecCmd), @@ -162,7 +162,7 @@ macro_rules! substrate_cli_subcommands { } } - fn base_path(&self) -> $crate::Result<::std::option::Option<::std::path::PathBuf>> { + fn base_path(&self) -> $crate::Result<::std::option::Option> { match self { $($enum::$variant(cmd) => cmd.base_path()),* } @@ -409,4 +409,3 @@ macro_rules! substrate_cli_subcommands { substrate_cli_subcommands!( Subcommand => BuildSpec, ExportBlocks, ImportBlocks, CheckBlock, Revert, PurgeChain, ExportState ); - diff --git a/client/cli/src/commands/purge_chain_cmd.rs b/client/cli/src/commands/purge_chain_cmd.rs index 9d364a45f7d09..053f427309828 100644 --- a/client/cli/src/commands/purge_chain_cmd.rs +++ b/client/cli/src/commands/purge_chain_cmd.rs @@ -26,7 +26,7 @@ use std::io::{self, Write}; use structopt::StructOpt; /// The `purge-chain` command used to remove the whole chain. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct PurgeChainCmd { /// Skip interactive prompt by answering yes automatically. #[structopt(short = "y")] diff --git a/client/cli/src/commands/revert_cmd.rs b/client/cli/src/commands/revert_cmd.rs index 6117eaf4880bf..1b5489df708a7 100644 --- a/client/cli/src/commands/revert_cmd.rs +++ b/client/cli/src/commands/revert_cmd.rs @@ -25,7 +25,7 @@ use std::fmt::Debug; use structopt::StructOpt; /// The `revert` command used revert the chain to a previous state. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct RevertCmd { /// Number of blocks to revert. #[structopt(default_value = "256")] diff --git a/client/cli/src/commands/run_cmd.rs b/client/cli/src/commands/run_cmd.rs index 23a410d679b8f..82d40e6a73f07 100644 --- a/client/cli/src/commands/run_cmd.rs +++ b/client/cli/src/commands/run_cmd.rs @@ -21,13 +21,13 @@ use crate::error::{Error, Result}; use crate::params::ImportParams; use crate::params::KeystoreParams; use crate::params::NetworkParams; +use crate::params::OffchainWorkerParams; use crate::params::SharedParams; use crate::params::TransactionPoolParams; -use crate::params::OffchainWorkerParams; use crate::CliConfiguration; use regex::Regex; use sc_service::{ - config::{MultiaddrWithPeerId, PrometheusConfig, TransactionPoolOptions}, + config::{BasePath, MultiaddrWithPeerId, PrometheusConfig, TransactionPoolOptions}, ChainSpec, Role, }; use sc_telemetry::TelemetryEndpoints; @@ -35,7 +35,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use structopt::StructOpt; /// The `run` command used to run a node. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct RunCmd { /// Enable validator mode. /// @@ -250,6 +250,16 @@ pub struct RunCmd { conflicts_with_all = &[ "sentry", "public-addr" ] )] pub sentry_nodes: Vec, + + /// Run a temporary node. + /// + /// A temporary directory will be created to store the configuration and will be deleted + /// at the end of the process. + /// + /// Note: the directory is random per process execution. This directory is used as base path + /// which includes: database, node key and keystore. + #[structopt(long, conflicts_with = "base-path")] + pub tmp: bool, } impl RunCmd { @@ -446,6 +456,14 @@ impl CliConfiguration for RunCmd { fn max_runtime_instances(&self) -> Result> { Ok(self.max_runtime_instances.map(|x| x.min(256))) } + + fn base_path(&self) -> Result> { + Ok(if self.tmp { + Some(BasePath::new_temp_dir()?) + } else { + self.shared_params().base_path() + }) + } } /// Check whether a node name is considered as valid. diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index a1ee1b0cc1da9..1374e75daf9c3 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -27,9 +27,9 @@ use crate::{ use names::{Generator, Name}; use sc_client_api::execution_extensions::ExecutionStrategies; use sc_service::config::{ - Configuration, DatabaseConfig, ExtTransport, KeystoreConfig, NetworkConfiguration, - NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, Role, RpcMethods, - TaskType, TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod, + BasePath, Configuration, DatabaseConfig, ExtTransport, KeystoreConfig, NetworkConfiguration, + NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, Role, RpcMethods, TaskType, + TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod, }; use sc_service::{ChainSpec, TracingReceiver}; use std::future::Future; @@ -87,7 +87,7 @@ pub trait CliConfiguration: Sized { /// Get the base path of the configuration (if any) /// /// By default this is retrieved from `SharedParams`. - fn base_path(&self) -> Result> { + fn base_path(&self) -> Result> { Ok(self.shared_params().base_path()) } @@ -402,14 +402,12 @@ pub trait CliConfiguration: Sized { let is_dev = self.is_dev()?; let chain_id = self.chain_id(is_dev)?; let chain_spec = cli.load_spec(chain_id.as_str())?; - let config_dir = self + let base_path = self .base_path()? - .unwrap_or_else(|| { - directories::ProjectDirs::from("", "", C::executable_name()) - .expect("app directories exist on all supported platforms; qed") - .data_local_dir() - .into() - }) + .unwrap_or_else(|| BasePath::from_project("", "", C::executable_name())); + let config_dir = base_path + .path() + .to_path_buf() .join("chains") .join(chain_spec.id()); let net_config_dir = config_dir.join(DEFAULT_NETWORK_CONFIG_PATH); @@ -464,6 +462,7 @@ pub trait CliConfiguration: Sized { max_runtime_instances, announce_block: self.announce_block()?, role, + base_path: Some(base_path), }) } @@ -507,5 +506,5 @@ pub fn generate_node_name() -> String { if count < NODE_NAME_MAX_LENGTH { return node_name; } - }; + } } diff --git a/client/cli/src/params/database_params.rs b/client/cli/src/params/database_params.rs index 3ff8eb01d0643..24b23f6076a02 100644 --- a/client/cli/src/params/database_params.rs +++ b/client/cli/src/params/database_params.rs @@ -20,7 +20,7 @@ use crate::arg_enums::Database; use structopt::StructOpt; /// Parameters for block import. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct DatabaseParams { /// Select database backend to use. #[structopt( diff --git a/client/cli/src/params/import_params.rs b/client/cli/src/params/import_params.rs index fb683df6d3be9..101189bec4448 100644 --- a/client/cli/src/params/import_params.rs +++ b/client/cli/src/params/import_params.rs @@ -27,7 +27,7 @@ use sc_client_api::execution_extensions::ExecutionStrategies; use structopt::StructOpt; /// Parameters for block import. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ImportParams { #[allow(missing_docs)] #[structopt(flatten)] @@ -130,7 +130,7 @@ impl ImportParams { } /// Execution strategies parameters. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ExecutionStrategiesParams { /// The means of execution used when calling into the runtime while syncing blocks. #[structopt( diff --git a/client/cli/src/params/keystore_params.rs b/client/cli/src/params/keystore_params.rs index 2fd610377d793..840cc51dff33f 100644 --- a/client/cli/src/params/keystore_params.rs +++ b/client/cli/src/params/keystore_params.rs @@ -26,7 +26,7 @@ use structopt::StructOpt; const DEFAULT_KEYSTORE_CONFIG_PATH: &'static str = "keystore"; /// Parameters of the keystore -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct KeystoreParams { /// Specify custom keystore path. #[structopt(long = "keystore-path", value_name = "PATH", parse(from_os_str))] diff --git a/client/cli/src/params/mod.rs b/client/cli/src/params/mod.rs index 3a66e5f05588a..f648337ed0af6 100644 --- a/client/cli/src/params/mod.rs +++ b/client/cli/src/params/mod.rs @@ -39,7 +39,7 @@ pub use crate::params::shared_params::*; pub use crate::params::transaction_pool_params::*; /// Wrapper type of `String` that holds an unsigned integer of arbitrary size, formatted as a decimal. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct BlockNumber(String); impl FromStr for BlockNumber { @@ -72,7 +72,7 @@ impl BlockNumber { } /// Wrapper type that is either a `Hash` or the number of a `Block`. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct BlockNumberOrHash(String); impl FromStr for BlockNumberOrHash { diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index c1639ad2b4370..2e0a6f1973064 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -26,7 +26,7 @@ use std::path::PathBuf; use structopt::StructOpt; /// Parameters used to create the network configuration. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct NetworkParams { /// Specify a list of bootnodes. #[structopt(long = "bootnodes", value_name = "ADDR")] diff --git a/client/cli/src/params/node_key_params.rs b/client/cli/src/params/node_key_params.rs index 7d19971ad6470..689cc6c681c83 100644 --- a/client/cli/src/params/node_key_params.rs +++ b/client/cli/src/params/node_key_params.rs @@ -31,7 +31,7 @@ const NODE_KEY_ED25519_FILE: &str = "secret_ed25519"; /// Parameters used to create the `NodeKeyConfig`, which determines the keypair /// used for libp2p networking. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct NodeKeyParams { /// The secret key to use for libp2p networking. /// diff --git a/client/cli/src/params/offchain_worker_params.rs b/client/cli/src/params/offchain_worker_params.rs index ca99ab506e484..f8d48edc4729d 100644 --- a/client/cli/src/params/offchain_worker_params.rs +++ b/client/cli/src/params/offchain_worker_params.rs @@ -32,7 +32,7 @@ use crate::OffchainWorkerEnabled; /// Offchain worker related parameters. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct OffchainWorkerParams { /// Should execute offchain workers on every block. /// diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index 36179359063e7..7db808e6d8f2f 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -21,7 +21,7 @@ use sc_service::{PruningMode, Role}; use structopt::StructOpt; /// Parameters to define the pruning mode -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct PruningParams { /// Specify the state pruning mode, a number of blocks to keep or 'archive'. /// diff --git a/client/cli/src/params/shared_params.rs b/client/cli/src/params/shared_params.rs index e9440f38a1fad..ad9ab04070563 100644 --- a/client/cli/src/params/shared_params.rs +++ b/client/cli/src/params/shared_params.rs @@ -16,11 +16,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use sc_service::config::BasePath; use std::path::PathBuf; use structopt::StructOpt; /// Shared parameters used by all `CoreParams`. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct SharedParams { /// Specify the chain specification (one of dev, local, or staging). #[structopt(long, value_name = "CHAIN_SPEC")] @@ -31,12 +32,7 @@ pub struct SharedParams { pub dev: bool, /// Specify custom base path. - #[structopt( - long, - short = "d", - value_name = "PATH", - parse(from_os_str) - )] + #[structopt(long, short = "d", value_name = "PATH", parse(from_os_str))] pub base_path: Option, /// Sets a custom logging filter. Syntax is =, e.g. -lsync=debug. @@ -49,8 +45,8 @@ pub struct SharedParams { impl SharedParams { /// Specify custom base path. - pub fn base_path(&self) -> Option { - self.base_path.clone() + pub fn base_path(&self) -> Option { + self.base_path.clone().map(Into::into) } /// Specify the development chain. diff --git a/client/cli/src/params/transaction_pool_params.rs b/client/cli/src/params/transaction_pool_params.rs index 2283c0f39f9c7..3ad278426922e 100644 --- a/client/cli/src/params/transaction_pool_params.rs +++ b/client/cli/src/params/transaction_pool_params.rs @@ -20,7 +20,7 @@ use sc_service::config::TransactionPoolOptions; use structopt::StructOpt; /// Parameters used to create the pool configuration. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct TransactionPoolParams { /// Maximum number of transactions in the transaction pool. #[structopt(long = "pool-limit", value_name = "COUNT", default_value = "8192")] diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index 409772d7ca1fa..b068af0166817 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -271,6 +271,10 @@ impl Runner { // and drop the runtime first. let _telemetry = service.telemetry(); + // we hold a reference to the base path so if the base path is a temporary directory it will + // not be deleted before the tokio runtime finish to clean up + let _base_path = service.base_path(); + { let f = service.fuse(); self.tokio_runtime diff --git a/client/service/Cargo.toml b/client/service/Cargo.toml index f3687a2b8a660..71e8e74c4c7f6 100644 --- a/client/service/Cargo.toml +++ b/client/service/Cargo.toml @@ -83,6 +83,9 @@ netstat2 = "0.8.1" [target.'cfg(target_os = "linux")'.dependencies] procfs = '0.7.8' +[target.'cfg(not(target_os = "unknown"))'.dependencies] +tempfile = "3.1.0" +directories = "2.0.2" [dev-dependencies] substrate-test-runtime-client = { version = "2.0.0-rc3", path = "../../test-utils/runtime/client" } diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 6e88042e367f0..c6cf8bf5df626 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -1418,6 +1418,7 @@ ServiceBuilder< keystore, marker: PhantomData::, prometheus_registry: config.prometheus_config.map(|config| config.registry), + _base_path: config.base_path.map(Arc::new), }) } } diff --git a/client/service/src/config.rs b/client/service/src/config.rs index cc9c742ed68db..2d4dc9dc2e90a 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -24,12 +24,14 @@ pub use sc_network::config::{ExtTransport, MultiaddrWithPeerId, NetworkConfigura pub use sc_executor::WasmExecutionMethod; use sc_client_api::execution_extensions::ExecutionStrategies; -use std::{future::Future, path::{PathBuf, Path}, pin::Pin, net::SocketAddr, sync::Arc}; +use std::{io, future::Future, path::{PathBuf, Path}, pin::Pin, net::SocketAddr, sync::Arc}; pub use sc_transaction_pool::txpool::Options as TransactionPoolOptions; use sc_chain_spec::ChainSpec; use sp_core::crypto::Protected; pub use sc_telemetry::TelemetryEndpoints; use prometheus_endpoint::Registry; +#[cfg(not(target_os = "unknown"))] +use tempfile::TempDir; /// Service configuration. pub struct Configuration { @@ -102,6 +104,8 @@ pub struct Configuration { pub max_runtime_instances: usize, /// Announce block automatically after they have been imported pub announce_block: bool, + /// Base path of the configuration + pub base_path: Option, } /// Type for tasks spawned by the executor. @@ -191,3 +195,59 @@ impl Default for RpcMethods { RpcMethods::Auto } } + +/// The base path that is used for everything that needs to be write on disk to run a node. +pub enum BasePath { + /// A temporary directory is used as base path and will be deleted when dropped. + #[cfg(not(target_os = "unknown"))] + Temporary(TempDir), + /// A path on the disk. + Permanenent(PathBuf), +} + +impl BasePath { + /// Create a `BasePath` instance using a temporary directory prefixed with "substrate" and use + /// it as base path. + /// + /// Note: the temporary directory will be created automatically and deleted when the `BasePath` + /// instance is dropped. + #[cfg(not(target_os = "unknown"))] + pub fn new_temp_dir() -> io::Result { + Ok(BasePath::Temporary( + tempfile::Builder::new().prefix("substrate").tempdir()?, + )) + } + + /// Create a `BasePath` instance based on an existing path on disk. + /// + /// Note: this function will not ensure that the directory exist nor create the directory. It + /// will also not delete the directory when the instance is dropped. + pub fn new>(path: P) -> BasePath { + BasePath::Permanenent(path.as_ref().to_path_buf()) + } + + /// Create a base path from values describing the project. + #[cfg(not(target_os = "unknown"))] + pub fn from_project(qualifier: &str, organization: &str, application: &str) -> BasePath { + BasePath::new( + directories::ProjectDirs::from(qualifier, organization, application) + .expect("app directories exist on all supported platforms; qed") + .data_local_dir(), + ) + } + + /// Retrieve the base path. + pub fn path(&self) -> &Path { + match self { + #[cfg(not(target_os = "unknown"))] + BasePath::Temporary(temp_dir) => temp_dir.path(), + BasePath::Permanenent(path) => path.as_path(), + } + } +} + +impl std::convert::From for BasePath { + fn from(path: PathBuf) -> Self { + BasePath::new(path) + } +} diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 67ac7bdb4fbd7..fc0567e268260 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -66,7 +66,7 @@ pub use self::builder::{ ServiceBuilder, ServiceBuilderCommand, TFullClient, TLightClient, TFullBackend, TLightBackend, TFullCallExecutor, TLightCallExecutor, RpcExtensionBuilder, }; -pub use config::{Configuration, DatabaseConfig, PruningMode, Role, RpcMethods, TaskType}; +pub use config::{BasePath, Configuration, DatabaseConfig, PruningMode, Role, RpcMethods, TaskType}; pub use sc_chain_spec::{ ChainSpec, GenericChainSpec, Properties, RuntimeGenesis, Extension as ChainSpecExtension, NoExtension, ChainType, @@ -110,14 +110,14 @@ pub struct Service { task_manager: TaskManager, select_chain: Option, network: Arc, - /// Sinks to propagate network status updates. - /// For each element, every time the `Interval` fires we push an element on the sender. + // Sinks to propagate network status updates. + // For each element, every time the `Interval` fires we push an element on the sender. network_status_sinks: Arc>>, transaction_pool: Arc, - /// Send a signal when a spawned essential task has concluded. The next time - /// the service future is polled it should complete with an error. + // Send a signal when a spawned essential task has concluded. The next time + // the service future is polled it should complete with an error. essential_failed_tx: TracingUnboundedSender<()>, - /// A receiver for spawned essential-tasks concluding. + // A receiver for spawned essential-tasks concluding. essential_failed_rx: TracingUnboundedReceiver<()>, rpc_handlers: sc_rpc_server::RpcHandler, _rpc: Box, @@ -127,6 +127,9 @@ pub struct Service { keystore: sc_keystore::KeyStorePtr, marker: PhantomData, prometheus_registry: Option, + // The base path is kept here because it can be a temporary directory which will be deleted + // when dropped + _base_path: Option>, } impl Unpin for Service {} @@ -210,6 +213,9 @@ pub trait AbstractService: Future> + Send + Unpin + S /// Get the prometheus metrics registry, if available. fn prometheus_registry(&self) -> Option; + + /// Get a clone of the base_path + fn base_path(&self) -> Option>; } impl AbstractService for @@ -244,7 +250,7 @@ where } fn telemetry(&self) -> Option { - self._telemetry.as_ref().map(|t| t.clone()) + self._telemetry.clone() } fn keystore(&self) -> sc_keystore::KeyStorePtr { @@ -310,6 +316,10 @@ where fn prometheus_registry(&self) -> Option { self.prometheus_registry.clone() } + + fn base_path(&self) -> Option> { + self._base_path.clone() + } } impl Future for diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 206153082505c..613b0d71ce933 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -34,7 +34,7 @@ use sc_service::{ GenericChainSpec, ChainSpecExtension, Configuration, - config::{DatabaseConfig, KeystoreConfig}, + config::{BasePath, DatabaseConfig, KeystoreConfig}, RuntimeGenesis, Role, Error, @@ -210,6 +210,7 @@ fn node_config