From e303b2103e21d00a86f452e0ae592d28d31c5e2f Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Tue, 17 Sep 2024 12:00:10 +0200 Subject: [PATCH 01/16] Change solochain flag into subcommand --- Cargo.lock | 4 + Cargo.toml | 2 + client/consensus/src/collators/lookahead.rs | 58 ++- client/consensus/src/mocks.rs | 8 +- client/service-container-chain/Cargo.toml | 2 + client/service-container-chain/src/cli.rs | 102 +++++- client/service-container-chain/src/service.rs | 19 +- client/service-container-chain/src/spawner.rs | 4 +- node/Cargo.toml | 4 +- node/src/cli.rs | 46 ++- node/src/command.rs | 106 +++++- node/src/command/solochain.rs | 332 ++++++++++++++++++ node/src/service.rs | 277 ++++++++++----- test/configs/zombieStarlight.json | 18 +- 14 files changed, 842 insertions(+), 140 deletions(-) create mode 100644 node/src/command/solochain.rs diff --git a/Cargo.lock b/Cargo.lock index fadc739c6..74a74d89d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17063,6 +17063,7 @@ dependencies = [ "async-io 1.13.0", "async-trait", "ccp-authorities-noting-inherent", + "chrono", "clap 4.5.4", "cumulus-client-cli", "cumulus-client-collator", @@ -17080,6 +17081,7 @@ dependencies = [ "dp-container-chain-genesis-data", "dp-slot-duration-runtime-api", "exit-future", + "fdlimit", "flashbox-runtime", "flume 0.10.14", "frame-benchmarking", @@ -17499,6 +17501,7 @@ dependencies = [ "async-trait", "ccp-authorities-noting-inherent", "clap 4.5.4", + "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -17557,6 +17560,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 563cc962f..9fcee947f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -330,9 +330,11 @@ tap = "1.0.1" # General (client) async-io = "1.3" async-trait = "0.1" +chrono = "0.4.31" clap = { version = "4.5.3", default-features = false, features = [ "derive" ] } core_extensions = "1.5.3" exit-future = { version = "0.2.0" } +fdlimit = "0.3.0" flume = "0.10.9" fs2 = "0.4.3" futures = { version = "0.3.1" } diff --git a/client/consensus/src/collators/lookahead.rs b/client/consensus/src/collators/lookahead.rs index 957d2e351..bb0f06ace 100644 --- a/client/consensus/src/collators/lookahead.rs +++ b/client/consensus/src/collators/lookahead.rs @@ -343,9 +343,41 @@ pub struct Params< pub authoring_duration: Duration, pub force_authoring: bool, pub cancellation_token: CancellationToken, - pub orchestrator_tx_pool: Arc, - pub orchestrator_client: Arc, - pub solochain: bool, + pub buy_core_params: BuyCoreParams, +} + +pub enum BuyCoreParams { + Orchestrator { + orchestrator_tx_pool: Arc, + orchestrator_client: Arc, + }, + Solochain { + // TODO: relay_tx_pool + }, +} + +impl Clone for BuyCoreParams { + fn clone(&self) -> Self { + match self { + Self::Orchestrator { + orchestrator_tx_pool, + orchestrator_client, + } => Self::Orchestrator { + orchestrator_tx_pool: orchestrator_tx_pool.clone(), + orchestrator_client: orchestrator_client.clone(), + }, + Self::Solochain {} => Self::Solochain {}, + } + } +} + +impl BuyCoreParams { + pub fn is_solochain(&self) -> bool { + match self { + BuyCoreParams::Solochain { .. } => true, + _ => false, + } + } } /// Run async-backing-friendly for Tanssi Aura. @@ -637,12 +669,20 @@ where let slot = inherent_providers.slot(); let container_chain_slot_duration = (params.get_current_slot_duration)(parent_header.hash()); - let buy_core_result = if params.solochain { - // TODO: implement parathread support for solochain - log::warn!("Unimplemented: cannot buy core for parathread in solochain"); - break; - } else { - try_to_buy_core::<_, _, <::Header as HeaderT>::Number, _, CIDP, _, _>(params.para_id, aux_data, inherent_providers, ¶ms.keystore, params.orchestrator_client.clone(), params.orchestrator_tx_pool.clone(), parent_header, params.orchestrator_slot_duration, container_chain_slot_duration).await + let buy_core_result = match ¶ms.buy_core_params { + BuyCoreParams::Orchestrator { + orchestrator_client, + orchestrator_tx_pool, + } => { + try_to_buy_core::<_, _, <::Header as HeaderT>::Number, _, CIDP, _, _>(params.para_id, aux_data, inherent_providers, ¶ms.keystore, orchestrator_client.clone(), orchestrator_tx_pool.clone(), parent_header, params.orchestrator_slot_duration, container_chain_slot_duration).await + } + BuyCoreParams::Solochain { + + } => { + // TODO: implement parathread support for solochain + log::warn!("Unimplemented: cannot buy core for parathread in solochain"); + break; + } }; match buy_core_result { Ok(block_hash) => { diff --git a/client/consensus/src/mocks.rs b/client/consensus/src/mocks.rs index cdec9a1e8..9cd96a45b 100644 --- a/client/consensus/src/mocks.rs +++ b/client/consensus/src/mocks.rs @@ -512,6 +512,7 @@ impl sc_consensus::Verifier for SealExtractorVerfier { } } +use crate::collators::lookahead::BuyCoreParams; use { cumulus_primitives_core::relay_chain::ValidationCodeHash, polkadot_node_subsystem::{overseer, OverseerSignal}, @@ -984,10 +985,11 @@ impl CollatorLookaheadTestBuilder { para_client: environ.clone().into(), sync_oracle: DummyOracle, para_backend: backend, - orchestrator_client: environ.into(), + buy_core_params: BuyCoreParams::Orchestrator { + orchestrator_client: environ.into(), + orchestrator_tx_pool: orchestrator_tx_pool.clone(), + }, orchestrator_slot_duration: SlotDuration::from_millis(SLOT_DURATION_MS), - orchestrator_tx_pool: orchestrator_tx_pool.clone(), - solochain: false, }; let (fut, exit_notification_receiver) = crate::collators::lookahead::run::< _, diff --git a/client/service-container-chain/Cargo.toml b/client/service-container-chain/Cargo.toml index 33e9db891..aa56274bf 100644 --- a/client/service-container-chain/Cargo.toml +++ b/client/service-container-chain/Cargo.toml @@ -20,6 +20,7 @@ log = { workspace = true } serde = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true } +url = { workspace = true } # Local ccp-authorities-noting-inherent = { workspace = true, features = [ "std" ] } @@ -70,6 +71,7 @@ sp-timestamp = { workspace = true, features = [ "std" ] } polkadot-primitives = { workspace = true } # Cumulus +cumulus-client-cli = { workspace = true } cumulus-client-collator = { workspace = true } cumulus-client-consensus-aura = { workspace = true } cumulus-client-consensus-common = { workspace = true } diff --git a/client/service-container-chain/src/cli.rs b/client/service-container-chain/src/cli.rs index 460efcad3..57aa0f1d8 100644 --- a/client/service-container-chain/src/cli.rs +++ b/client/service-container-chain/src/cli.rs @@ -14,8 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see +use sc_cli::CliConfiguration; +use url::Url; use { crate::chain_spec::RawGenesisConfig, + cumulus_client_cli::{CollatorOptions, RelayChainMode}, dc_orchestrator_chain_interface::ContainerChainGenesisData, dp_container_chain_genesis_data::json::properties_to_map, sc_chain_spec::ChainSpec, @@ -45,6 +48,76 @@ pub struct ContainerChainRunCmd { /// Keep container-chain db after changing collator assignments #[arg(long)] pub keep_db: bool, + + /// Creates a less resource-hungry node that retrieves relay chain data from an RPC endpoint. + /// + /// The provided URLs should point to RPC endpoints of the relay chain. + /// This node connects to the remote nodes following the order they were specified in. If the + /// connection fails, it attempts to connect to the next endpoint in the list. + /// + /// Note: This option doesn't stop the node from connecting to the relay chain network but + /// reduces bandwidth use. + #[arg( + long, + value_parser = validate_relay_chain_url, + num_args = 0.., + alias = "relay-chain-rpc-url" + )] + pub relay_chain_rpc_urls: Vec, + + /// EXPERIMENTAL: Embed a light client for the relay chain. Only supported for full-nodes. + /// Will use the specified relay chain chainspec. + #[arg(long, conflicts_with_all = ["relay_chain_rpc_urls", "collator"])] + pub relay_chain_light_client: bool, +} + +impl ContainerChainRunCmd { + /// Create a [`NormalizedRunCmd`] which merges the `collator` cli argument into `validator` to + /// have only one. + // Copied from polkadot-sdk/cumulus/client/cli/src/lib.rs + // TODO: deduplicate this function and [ContainerChainCli::new] + pub fn normalize(&self) -> ContainerChainCli { + let mut new_base = self.clone(); + + new_base.base.validator = self.base.validator || self.collator; + + // Append `containers/` to base_path for this object. This is to ensure that when spawning + // a new container chain, its database is always inside the `containers` folder. + // So if the user passes `--base-path /tmp/node`, we want the ephemeral container data in + // `/tmp/node/containers`, and the persistent storage in `/tmp/node/config`. + // TODO: there should be a way to avoid this if we refactor the code that creates the db, + // but maybe that breaks dancebox + let base_path = match self.base.base_path() { + Ok(Some(x)) => x, + _ => { + // This is maybe unreachable. There is always a default base path, and if run in + // `--dev` or `--tmp` mode, a temporary base path is created. + panic!("No base path") + } + }; + + let base_path = base_path.path().join("containers"); + + ContainerChainCli { + base: new_base, + preloaded_chain_spec: None, + } + } + + /// Create [`CollatorOptions`] representing options only relevant to parachain collator nodes + // Copied from polkadot-sdk/cumulus/client/cli/src/lib.rs + pub fn collator_options(&self) -> CollatorOptions { + let relay_chain_mode = match ( + self.relay_chain_light_client, + !self.relay_chain_rpc_urls.is_empty(), + ) { + (true, _) => RelayChainMode::LightClient, + (_, true) => RelayChainMode::ExternalRpc(self.relay_chain_rpc_urls.clone()), + _ => RelayChainMode::Embedded, + }; + + CollatorOptions { relay_chain_mode } + } } #[derive(Debug)] @@ -52,9 +125,6 @@ pub struct ContainerChainCli { /// The actual container chain cli object. pub base: ContainerChainRunCmd, - /// The base path that should be used by the container chain. - pub base_path: PathBuf, - /// The ChainSpecs that this struct can initialize. This starts empty and gets filled /// by calling preload_chain_spec_file. pub preloaded_chain_spec: Option>, @@ -64,7 +134,6 @@ impl Clone for ContainerChainCli { fn clone(&self) -> Self { Self { base: self.base.clone(), - base_path: self.base_path.clone(), preloaded_chain_spec: self.preloaded_chain_spec.as_ref().map(|x| x.cloned_box()), } } @@ -76,11 +145,17 @@ impl ContainerChainCli { para_config: &sc_service::Configuration, container_chain_args: impl Iterator, ) -> Self { + let mut base: ContainerChainRunCmd = clap::Parser::parse_from(container_chain_args); + + // Copy some parachain args into container chain args + // TODO: warn the user if they try to set one of these args using container chain args, + // because that args will be ignored let base_path = para_config.base_path.path().join("containers"); + base.base.shared_params.base_path = Some(base_path); + // TODO: move wasmtime_precompiled here Self { - base_path, - base: clap::Parser::parse_from(container_chain_args), + base, preloaded_chain_spec: None, } } @@ -366,3 +441,18 @@ fn parse_container_chain_id_str(id: &str) -> std::result::Result { }) .ok_or_else(|| format!("load_spec called with invalid id: {:?}", id)) } + +// Copied from polkadot-sdk/cumulus/client/cli/src/lib.rs +fn validate_relay_chain_url(arg: &str) -> Result { + let url = Url::parse(arg).map_err(|e| e.to_string())?; + + let scheme = url.scheme(); + if scheme == "ws" || scheme == "wss" { + Ok(url) + } else { + Err(format!( + "'{}' URL scheme not supported. Only websocket RPC is currently supported", + url.scheme() + )) + } +} diff --git a/client/service-container-chain/src/service.rs b/client/service-container-chain/src/service.rs index 6b88723a0..07111fc23 100644 --- a/client/service-container-chain/src/service.rs +++ b/client/service-container-chain/src/service.rs @@ -61,6 +61,7 @@ use { #[allow(deprecated)] use sc_executor::NativeElseWasmExecutor; +use tc_consensus::collators::lookahead::BuyCoreParams; type FullBackend = TFullBackend; @@ -330,7 +331,7 @@ fn start_consensus_container( u64::try_from(relay_slot_ms).expect("relay chain slot duration overflows u64"), ) } else { - cumulus_client_consensus_aura::slot_duration(&*orchestrator_client) + cumulus_client_consensus_aura::slot_duration(orchestrator_client.as_deref().unwrap()) .expect("start_consensus_container: slot duration should exist") }; @@ -365,6 +366,14 @@ fn start_consensus_container( .map(polkadot_primitives::ValidationCode) .map(|c| c.hash()) }; + let buy_core_params = if solochain { + BuyCoreParams::Solochain {} + } else { + BuyCoreParams::Orchestrator { + orchestrator_tx_pool: orchestrator_tx_pool.unwrap(), + orchestrator_client: orchestrator_client.unwrap(), + } + }; let params = LookaheadTanssiAuraParams { get_current_slot_duration: move |block_hash| { @@ -481,7 +490,7 @@ fn start_consensus_container( })?; let authorities = tc_consensus::authorities::( - orchestrator_client_for_cidp.as_ref(), + orchestrator_client_for_cidp.as_ref().unwrap(), &latest_header.hash(), para_id, ); @@ -499,7 +508,7 @@ fn start_consensus_container( ); let slot_freq = tc_consensus::min_slot_freq::( - orchestrator_client_for_cidp.as_ref(), + orchestrator_client_for_cidp.as_ref().unwrap(), &latest_header.hash(), para_id, ); @@ -531,9 +540,7 @@ fn start_consensus_container( code_hash_provider, // This cancellation token is no-op as it is not shared outside. cancellation_token: CancellationToken::new(), - orchestrator_tx_pool, - orchestrator_client, - solochain, + buy_core_params, }; let (fut, _exit_notification_receiver) = diff --git a/client/service-container-chain/src/spawner.rs b/client/service-container-chain/src/spawner.rs index 83198e698..bcf4edc42 100644 --- a/client/service-container-chain/src/spawner.rs +++ b/client/service-container-chain/src/spawner.rs @@ -126,8 +126,8 @@ pub struct ContainerChainSpawnParams { #[derive(Clone)] pub struct CollationParams { pub collator_key: CollatorPair, - pub orchestrator_tx_pool: Arc>, - pub orchestrator_client: Arc, + pub orchestrator_tx_pool: Option>>, + pub orchestrator_client: Option>, pub orchestrator_para_id: ParaId, pub solochain: bool, } diff --git a/node/Cargo.toml b/node/Cargo.toml index 428ad2083..f829ad11f 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -13,8 +13,10 @@ workspace = true [dependencies] async-io = { workspace = true } async-trait = { workspace = true } +chrono = { workspace = true } clap = { workspace = true, features = [ "derive" ] } exit-future = { workspace = true } +fdlimit = { workspace = true } flume = { workspace = true } fs2 = { workspace = true } futures = { workspace = true } @@ -79,7 +81,6 @@ sp-api = { workspace = true, features = [ "std" ] } sp-block-builder = { workspace = true } sp-blockchain = { workspace = true } sp-consensus = { workspace = true } - sp-consensus-aura = { workspace = true } sp-consensus-slots = { workspace = true } sp-core = { workspace = true, features = [ "std" ] } @@ -87,6 +88,7 @@ sp-inherents = { workspace = true, features = [ "std" ] } sp-io = { workspace = true, features = [ "std" ] } sp-keystore = { workspace = true, features = [ "std" ] } sp-offchain = { workspace = true, features = [ "std" ] } +sp-panic-handler = { workspace = true } sp-runtime = { workspace = true, features = [ "std" ] } sp-session = { workspace = true, features = [ "std" ] } sp-state-machine = { workspace = true, features = [ "std" ] } diff --git a/node/src/cli.rs b/node/src/cli.rs index e644fe9ee..b9efc4b14 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see . +use tc_service_container_chain::cli::ContainerChainRunCmd; use { node_common::service::Sealing, sc_cli::{CliConfiguration, NodeKeyParams, SharedParams}, @@ -63,10 +64,47 @@ pub enum Subcommand { /// Precompile the WASM runtime into native code PrecompileWasm(sc_cli::PrecompileWasmCmd), + + /// Solochain collator mode + SoloChain(SoloChainCmd), +} + +/// The `build-spec` command used to build a specification. +#[derive(Debug, clap::Parser)] +#[group(skip)] +pub struct SoloChainCmd { + #[command(flatten)] + pub run: ContainerChainRunCmd, + + /// Disable automatic hardware benchmarks. + /// + /// By default these benchmarks are automatically ran at startup and measure + /// the CPU speed, the memory bandwidth and the disk speed. + /// + /// The results are then printed out in the logs, and also sent as part of + /// telemetry, if telemetry is enabled. + #[arg(long)] + pub no_hardware_benchmarks: bool, + + /* + /// Enable the development service to run without a backing relay chain + #[arg(long)] + pub dev_service: bool, + + /// When blocks should be sealed in the dev service. + /// + /// Options are "instant", "manual", or timer interval in milliseconds + #[arg(long, default_value = "instant")] + pub sealing: Sealing, + */ + /// Relay chain arguments + #[arg(raw = true)] + pub relay_chain_args: Vec, } /// The `build-spec` command used to build a specification. #[derive(Debug, Clone, clap::Parser)] +#[group(skip)] pub struct BuildSpecCmd { #[clap(flatten)] pub base: sc_cli::BuildSpecCmd, @@ -163,7 +201,7 @@ impl KeyCmd { #[derive(Debug, clap::Parser)] #[command( - propagate_version = true, + propagate_version = false, // setting this to true makes the verify_cli test fail args_conflicts_with_subcommands = true, subcommand_negates_reqs = true )] @@ -248,3 +286,9 @@ impl RelayChainCli { } } } + +#[test] +fn verify_cli() { + use clap::CommandFactory; + Cli::command().debug_assert(); +} diff --git a/node/src/command.rs b/node/src/command.rs index fa1ac9dd0..76907d5a6 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see . +use crate::command::solochain::NotParachainConfiguration; use { crate::{ chain_spec, @@ -40,6 +41,8 @@ use { tc_service_container_chain::{chain_spec::RawChainSpec, cli::ContainerChainCli}, }; +pub mod solochain; + fn load_spec( id: &str, para_id: Option, @@ -338,6 +341,93 @@ pub fn run() -> Result<()> { )) }) } + Some(Subcommand::SoloChain(cmd)) => { + // Cannot use create_configuration function because that needs a chain spec. + // So write our own `create_runner` function that doesn't need chain spec. + let normalized_run = cmd.run.normalize(); + let runner = solochain::create_runner(&cli, &normalized_run)?; + + // TODO: Assert that there are no flags between `tanssi-node` and `solo-chain`. + // These will be ignored anyway. + // We need to do this after create_runner because before that logging is not setup yet + // Zombienet appends a --chain flag after "solo-chain" subcommand, which is ignored, + if cmd.run.base.shared_params.chain.is_some() { + log::warn!( + "Ignoring --chain argument: solochain mode does only need the relay chain-spec" + ); + } + + let collator_options = cmd.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + /* + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()] + .iter() + .chain(cmd.relay_chain_args.iter()), + ); + */ + // TODO: refactor this into function that returns `RelayChainCli` + let binding = [RelayChainCli::executable_name()]; + let relay_chain_args = binding.iter().chain(cmd.relay_chain_args.iter()); + let polkadot_cli = { + let base_path = config.base_path.path().join("polkadot"); + // TODO: where to get chain_id from? + let chain_id = Some("starlight_local_testnet".to_string()); + + RelayChainCli { + base_path, + chain_id, + base: clap::Parser::parse_from(relay_chain_args), + } + }; + + // TODO: dev mode does not make sense for starlight collator? + // But better to detect --dev flag and panic than ignore it + /* + let dev_service = config.chain_spec.is_dev() + || relay_chain_id == Some("dev-service".to_string()) + || cli_run_dev_service; + + if dev_service { + return crate::service::start_dev_node(config, cli_run_sealing, hwbench, id) + .map_err(Into::into); + } + */ + + let tokio_handle = config.tokio_handle.clone(); + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + // We need to bake in some container-chain args + let container_chain_cli = normalized_run; + let tokio_handle = config.tokio_handle.clone(); + let container_chain_config = (container_chain_cli, tokio_handle); + let not_config = NotParachainConfiguration { + chain_type: polkadot_config.chain_spec.chain_type(), + // relay_chain will be set to "starlight_local_testnet" + // But everywhere else it is hardcoded to "rococo-local" so not sure if it's used + relay_chain: polkadot_config.chain_spec.id().to_string(), + collator: container_chain_config.0.base.collator, + }; + + // TODO: we can't enable hwbench because we don't have a db. Find a workaround + let hwbench = None; + + crate::service::start_solochain_node( + not_config, + polkadot_config, + container_chain_config, + collator_options, + hwbench, + ) + .await + .map(|r| r.0) + .map_err(Into::into) + }) + } None => { let runner = cli.create_runner(&cli.run.normalize())?; let collator_options = cli.run.collator_options(); @@ -376,22 +466,6 @@ pub fn run() -> Result<()> { SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) .map_err(|err| format!("Relay chain argument error: {}", err))?; - let solo_chain = cli.run.solo_chain; - if solo_chain { - // We need to bake in some container-chain args - let container_chain_cli = ContainerChainCli::new( - &config, - [ContainerChainCli::executable_name()].iter().chain(cli.container_chain_args().iter()), - ); - let tokio_handle = config.tokio_handle.clone(); - let container_chain_config = (container_chain_cli, tokio_handle); - - return crate::service::start_solochain_node(config, polkadot_config, container_chain_config, collator_options, hwbench) - .await - .map(|r| r.0) - .map_err(Into::into); - } - let parachain_account = AccountIdConversion::::into_account_truncating(&id); diff --git a/node/src/command/solochain.rs b/node/src/command/solochain.rs new file mode 100644 index 000000000..d309799c6 --- /dev/null +++ b/node/src/command/solochain.rs @@ -0,0 +1,332 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +//! Helper functions used to implement solochain collator + +use crate::cli::Cli; +use futures::FutureExt; +use jsonrpsee::server::BatchRequestConfig; +use log::{info, warn}; +use sc_chain_spec::{ChainType, GenericChainSpec, NoExtension}; +use sc_cli::{CliConfiguration, DefaultConfigurationValues, Signals, SubstrateCli}; +use sc_network::config::{NetworkBackendType, NetworkConfiguration, TransportConfig}; +use sc_network_common::role::Role; +use sc_service::config::KeystoreConfig; +use sc_service::{BasePath, BlocksPruning, Configuration, DatabaseSource, TaskManager}; +use sc_tracing::logging::LoggerBuilder; +use std::future::Future; +use std::num::NonZeroUsize; +use std::time::Duration; +use tc_service_container_chain::cli::ContainerChainCli; + +pub struct SlimConfig { + pub tokio_handle: tokio::runtime::Handle, + pub base_path: BasePath, + pub network_node_name: String, + pub display_role: String, +} + +/// A Substrate CLI runtime that can be used to run a node or a command +pub struct NotRunner { + config: SlimConfig, + tokio_runtime: tokio::runtime::Runtime, + signals: Signals, +} + +impl NotRunner { + /// Log information about the node itself. + /// + /// # Example: + /// + /// ```text + /// 2020-06-03 16:14:21 Substrate Node + /// 2020-06-03 16:14:21 ✌️ version 2.0.0-rc3-f4940588c-x86_64-linux-gnu + /// 2020-06-03 16:14:21 ❤️ by Parity Technologies , 2017-2020 + /// 2020-06-03 16:14:21 📋 Chain specification: Flaming Fir + /// 2020-06-03 16:14:21 🏷 Node name: jolly-rod-7462 + /// 2020-06-03 16:14:21 👤 Role: FULL + /// 2020-06-03 16:14:21 💾 Database: RocksDb at /tmp/c/chains/flamingfir7/db + /// 2020-06-03 16:14:21 ⛓ Native runtime: node-251 (substrate-node-1.tx1.au10) + /// ``` + fn print_node_infos(&self) { + use chrono::offset::Local; + use chrono::Datelike; + type C = ContainerChainCli; + info!("{}", C::impl_name()); + info!("✌️ version {}", C::impl_version()); + info!( + "❤️ by {}, {}-{}", + C::author(), + C::copyright_start_year(), + Local::now().year() + ); + //info!("📋 Chain specification: {}", config.chain_spec.name()); + info!("🏷 Node name: {}", self.config.network_node_name); + info!("👤 Role: {}", self.config.display_role); + /* + info!( + "💾 Database: {} at {}", + config.database, + config + .database + .path() + .map_or_else(|| "".to_owned(), |p| p.display().to_string()) + ); + */ + info!( + "💾 Database: {} at {}", + "ParityDb", + // Print base path instead of db path because each container will have its own db in a + // different subdirectory. + self.config.base_path.path().display(), + ); + } + + /// A helper function that runs a node with tokio and stops if the process receives the signal + /// `SIGTERM` or `SIGINT`. + pub fn run_node_until_exit( + self, + initialize: impl FnOnce(SlimConfig) -> F, + ) -> std::result::Result<(), E> + where + F: Future>, + E: std::error::Error + Send + Sync + 'static + From, + { + self.print_node_infos(); + + let mut task_manager = self.tokio_runtime.block_on(initialize(self.config))?; + + let res = self + .tokio_runtime + .block_on(self.signals.run_until_signal(task_manager.future().fuse())); + // We need to drop the task manager here to inform all tasks that they should shut down. + // + // This is important to be done before we instruct the tokio runtime to shutdown. Otherwise + // the tokio runtime will wait the full 60 seconds for all tasks to stop. + let task_registry = task_manager.into_task_registry(); + + // Give all futures 60 seconds to shutdown, before tokio "leaks" them. + let shutdown_timeout = Duration::from_secs(60); + self.tokio_runtime.shutdown_timeout(shutdown_timeout); + + let running_tasks = task_registry.running_tasks(); + + if !running_tasks.is_empty() { + log::error!("Detected running(potentially stalled) tasks on shutdown:"); + running_tasks.iter().for_each(|(task, count)| { + let instances_desc = if *count > 1 { + format!("with {} instances ", count) + } else { + "".to_string() + }; + + if task.is_default_group() { + log::error!( + "Task \"{}\" was still running {}after waiting {} seconds to finish.", + task.name, + instances_desc, + shutdown_timeout.as_secs(), + ); + } else { + log::error!( + "Task \"{}\" (Group: {}) was still running {}after waiting {} seconds to finish.", + task.name, + task.group, + instances_desc, + shutdown_timeout.as_secs(), + ); + } + }); + } + + res.map_err(Into::into) + } +} + +/// Equivalent to [Cli::create_runner] +pub fn create_runner, DVC: DefaultConfigurationValues>( + cli: &Cli, + command: &T, +) -> sc_cli::Result { + let tokio_runtime = sc_cli::build_runtime()?; + + // `capture` needs to be called in a tokio context. + // Also capture them as early as possible. + let signals = tokio_runtime.block_on(async { Signals::capture() })?; + + init_cmd(command, &Cli::support_url(), &Cli::impl_version())?; + + let base_path = command.base_path()?.unwrap(); + let network_node_name = command.node_name()?; + let role = if cli.run.collator { + Role::Authority + } else { + Role::Full + }; + let config = SlimConfig { + tokio_handle: tokio_runtime.handle().clone(), + base_path, + network_node_name, + display_role: role.to_string(), + }; + + Ok(NotRunner { + config, + tokio_runtime, + signals, + }) +} + +/// The recommended open file descriptor limit to be configured for the process. +const RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT: u64 = 10_000; + +/// Equivalent to [CliConfiguration::init] +fn init_cmd, DVC: DefaultConfigurationValues>( + this: &T, + support_url: &String, + impl_version: &String, +) -> sc_cli::Result<()> { + sp_panic_handler::set(support_url, impl_version); + + let mut logger = LoggerBuilder::new(this.log_filters()?); + logger + .with_log_reloading(this.enable_log_reloading()?) + .with_detailed_output(this.detailed_log_output()?); + + if let Some(tracing_targets) = this.tracing_targets()? { + let tracing_receiver = this.tracing_receiver()?; + logger.with_profiling(tracing_receiver, tracing_targets); + } + + if this.disable_log_color()? { + logger.with_colors(false); + } + + logger.init()?; + + match fdlimit::raise_fd_limit() { + Ok(fdlimit::Outcome::LimitRaised { to, .. }) => { + if to < RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT { + warn!( + "Low open file descriptor limit configured for the process. \ + Current value: {:?}, recommended value: {:?}.", + to, RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT, + ); + } + } + Ok(fdlimit::Outcome::Unsupported) => { + // Unsupported platform (non-Linux) + } + Err(error) => { + warn!( + "Failed to configure file descriptor limit for the process: \ + {}, recommended value: {:?}.", + error, RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT, + ); + } + } + + Ok(()) +} + +// TODO: merge this with SlimConfig +pub struct NotParachainConfiguration { + pub chain_type: sc_chain_spec::ChainType, + pub relay_chain: String, + pub collator: bool, +} + +/// Create a dummy [Configuration] that should only be used as input to polkadot-sdk functions that +/// take this struct as input but only use one field of it. +/// This is needed because [Configuration] does not implement [Default]. +pub fn dummy_config(tokio_handle: tokio::runtime::Handle, base_path: BasePath) -> Configuration { + Configuration { + impl_name: "".to_string(), + impl_version: "".to_string(), + role: Role::Full, + tokio_handle, + transaction_pool: Default::default(), + network: NetworkConfiguration { + net_config_path: None, + listen_addresses: vec![], + public_addresses: vec![], + boot_nodes: vec![], + node_key: Default::default(), + default_peers_set: Default::default(), + default_peers_set_num_full: 0, + client_version: "".to_string(), + node_name: "".to_string(), + transport: TransportConfig::MemoryOnly, + max_parallel_downloads: 0, + max_blocks_per_request: 0, + sync_mode: Default::default(), + enable_dht_random_walk: false, + allow_non_globals_in_dht: false, + kademlia_disjoint_query_paths: false, + kademlia_replication_factor: NonZeroUsize::new(20).unwrap(), + ipfs_server: false, + yamux_window_size: None, + network_backend: NetworkBackendType::Libp2p, + }, + keystore: KeystoreConfig::InMemory, + database: DatabaseSource::ParityDb { + path: Default::default(), + }, + trie_cache_maximum_size: None, + state_pruning: None, + blocks_pruning: BlocksPruning::KeepAll, + chain_spec: Box::new( + GenericChainSpec::::builder(Default::default(), NoExtension::None) + .with_name("test") + .with_id("test_id") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(Default::default()) + .build(), + ), + wasm_method: Default::default(), + wasmtime_precompiled: None, + wasm_runtime_overrides: None, + rpc_addr: None, + rpc_max_connections: 0, + rpc_cors: None, + rpc_methods: Default::default(), + rpc_max_request_size: 0, + rpc_max_response_size: 0, + rpc_id_provider: None, + rpc_max_subs_per_conn: 0, + rpc_port: 0, + rpc_message_buffer_capacity: 0, + rpc_batch_config: BatchRequestConfig::Disabled, + rpc_rate_limit: None, + rpc_rate_limit_whitelisted_ips: vec![], + rpc_rate_limit_trust_proxy_headers: false, + prometheus_config: None, + telemetry_endpoints: None, + default_heap_pages: None, + offchain_worker: Default::default(), + force_authoring: false, + disable_grandpa: false, + dev_key_seed: None, + tracing_targets: None, + tracing_receiver: Default::default(), + max_runtime_instances: 0, + announce_block: false, + data_path: Default::default(), + base_path, + informant_output_format: Default::default(), + runtime_cache_size: 0, + } +} diff --git a/node/src/service.rs b/node/src/service.rs index 864b65c21..1b3042e45 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -16,6 +16,13 @@ //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. +use crate::command::solochain::{dummy_config, NotParachainConfiguration}; +use sc_cli::CliConfiguration; +use sc_network_common::role::Role; +use sc_service::config::KeystoreConfig; +use sc_service::KeystoreContainer; +use std::path::{Path, PathBuf}; +use tc_consensus::collators::lookahead::BuyCoreParams; use { cumulus_client_cli::CollatorOptions, cumulus_client_collator::service::CollatorService, @@ -453,8 +460,8 @@ async fn start_node_impl( data_preserver: false, collation_params: if validator { Some(spawner::CollationParams { - orchestrator_client: orchestrator_client.clone(), - orchestrator_tx_pool, + orchestrator_client: Some(orchestrator_client.clone()), + orchestrator_tx_pool: Some(orchestrator_tx_pool), orchestrator_para_id: para_id, collator_key: collator_key .expect("there should be a collator key if we're a validator"), @@ -558,6 +565,10 @@ fn start_consensus_orchestrator( }; let cancellation_token = CancellationToken::new(); + let buy_core_params = BuyCoreParams::Orchestrator { + orchestrator_tx_pool, + orchestrator_client: client.clone(), + }; let params = LookaheadTanssiAuraParams { get_current_slot_duration: move |block_hash| { @@ -639,7 +650,7 @@ fn start_consensus_orchestrator( } }, block_import, - para_client: client.clone(), + para_client: client, relay_client: relay_chain_interface, sync_oracle, keystore, @@ -655,9 +666,7 @@ fn start_consensus_orchestrator( code_hash_provider, para_backend: backend, cancellation_token: cancellation_token.clone(), - orchestrator_tx_pool, - orchestrator_client: client, - solochain: false, + buy_core_params, }; let (fut, exit_notification_receiver) = @@ -689,71 +698,179 @@ pub async fn start_parachain_node( .await } +/// Returns the default path for configuration directory based on the chain_spec +pub(crate) fn build_solochain_config_dir(base_path: &PathBuf) -> PathBuf { + // Original: Collator1000-01/chains/dancebox/ + //base_path.path().join("chains").join(chain_id) + // Starlight: Collator1000-01/config/ + let mut base_path = base_path.clone(); + // Remove "/containers" + base_path.pop(); + base_path.join("config") +} + +/// Returns the default path for the network configuration inside the configuration dir +pub(crate) fn build_solochain_net_config_dir(config_dir: &PathBuf) -> PathBuf { + config_dir.join("network") +} + +fn keystore_config( + keystore_params: Option<&sc_cli::KeystoreParams>, + config_dir: &PathBuf, +) -> sc_cli::Result { + keystore_params + .map(|x| x.keystore_config(config_dir)) + .unwrap_or_else(|| Ok(KeystoreConfig::InMemory)) +} + /// Start a solochain node. pub async fn start_solochain_node( // Parachain config not used directly, but we need it to derive the default values for some container_config args - orchestrator_config: Configuration, + orchestrator_config: NotParachainConfiguration, polkadot_config: Configuration, - mut container_chain_config: (ContainerChainCli, tokio::runtime::Handle), + container_chain_config: (ContainerChainCli, tokio::runtime::Handle), collator_options: CollatorOptions, hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc)> { +) -> sc_service::error::Result<(TaskManager, ())> { let orchestrator_para_id = Default::default(); - let parachain_config = prepare_node_config(orchestrator_config); - { - let (container_chain_cli, _) = &mut container_chain_config; - // If the container chain args have no --wasmtime-precompiled flag, use the same as the orchestrator - if container_chain_cli - .base - .base - .import_params - .wasmtime_precompiled - .is_none() - { - container_chain_cli - .base - .base - .import_params - .wasmtime_precompiled - .clone_from(¶chain_config.wasmtime_precompiled); - } - } + //orchestrator_config.announce_block = false; // prepare_node_config() + let parachain_config = orchestrator_config; - let chain_type: sc_chain_spec::ChainType = parachain_config.chain_spec.chain_type(); - let relay_chain = crate::chain_spec::Extensions::try_get(&*parachain_config.chain_spec) - .map(|e| e.relay_chain.clone()) - .ok_or("Could not find relay_chain extension in chain-spec.")?; + let chain_type: sc_chain_spec::ChainType = parachain_config.chain_type.clone(); + let relay_chain = parachain_config.relay_chain.clone(); // Channel to send messages to start/stop container chains let (cc_spawn_tx, cc_spawn_rx) = unbounded_channel(); - // Create a `NodeBuilder` which helps setup parachain nodes common systems. - let mut node_builder = NodeConfig::new_builder(¶chain_config, hwbench.clone())?; + let (telemetry_worker_handle, mut task_manager, keystore_container) = { + // TODO: instead of putting keystore in + // Collator1000-01/data/chains/simple_container_2000/keystore + // We want it in + // Collator1000-01/data/config/keystore + // And same for "network" folder + // But zombienet will put the keys in the old path, we need to manually copy it if we + // are running under zombienet + let config_dir = build_solochain_config_dir(&container_chain_config.0.base_path); + let _net_config_dir = build_solochain_net_config_dir(&config_dir); + let keystore = + keystore_config(container_chain_config.0.keystore_params(), &config_dir).unwrap(); - let (_block_import, import_queue) = import_queue(¶chain_config, &node_builder); + { + let keystore_path = keystore.path().unwrap(); + let mut zombienet_path = keystore_path.to_owned(); + // Collator1000-01/data/config/keystore/ + zombienet_path.pop(); + // Collator1000-01/data/config/ + zombienet_path.pop(); + // Collator1000-01/data/ + zombienet_path.push("chains/simple_container_2000/keystore/"); + // Collator1000-01/data/chains/simple_container_2000/keystore/ + + if zombienet_path.exists() { + // Copy to keystore folder + + // https://stackoverflow.com/a/65192210 + // TODO: maybe use a crate instead + // TODO: never overwrite files, only copy those that don't exist + fn copy_dir_all( + src: impl AsRef, + dst: impl AsRef, + files_copied: &mut u32, + ) -> std::io::Result<()> { + use std::fs; + fs::create_dir_all(&dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all( + entry.path(), + dst.as_ref().join(entry.file_name()), + files_copied, + )?; + } else { + fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + *files_copied += 1; + } + } + Ok(()) + } - let (relay_chain_interface, collator_key) = node_builder - .build_relay_chain_interface(¶chain_config, polkadot_config, collator_options.clone()) - .await?; + let mut files_copied = 0; + copy_dir_all(zombienet_path, keystore_path, &mut files_copied).unwrap(); + log::info!("Copied {} keys from zombienet keystore", files_copied); + } else { + log::warn!("Copy nimbus keys to {:?}", keystore_path); + } + } - let validator = parachain_config.role.is_authority(); + let keystore_container = KeystoreContainer::new(&keystore)?; - log::info!("start_solochain_node: is validator? {}", validator); + let task_manager = { + //let registry = config.prometheus_config.as_ref().map(|cfg| &cfg.registry); + let registry = None; + TaskManager::new(container_chain_config.1.clone(), registry)? + }; - let node_builder = node_builder - .build_cumulus_network::<_, sc_network::NetworkWorker<_, _>>( - ¶chain_config, - orchestrator_para_id, - import_queue, - relay_chain_interface.clone(), + /* + let telemetry = parachain_config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + + let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager + .spawn_handle() + .spawn("telemetry", None, worker.run()); + telemetry + }); + + */ + let telemetry_worker_handle = None; + + (telemetry_worker_handle, task_manager, keystore_container) + }; + + // Dummy parachain config only needed because `build_relay_chain_interface` needs to know if we + // are collators or not + let validator = parachain_config.collator; + let mut dummy_parachain_config = dummy_config( + polkadot_config.tokio_handle.clone(), + polkadot_config.base_path.clone(), + ); + dummy_parachain_config.role = if validator { + Role::Authority + } else { + Role::Full + }; + let (relay_chain_interface, collator_key) = + cumulus_client_service::build_relay_chain_interface( + polkadot_config, + &dummy_parachain_config, + telemetry_worker_handle.clone(), + &mut task_manager, + collator_options.clone(), + hwbench.clone(), ) - .await?; + .await + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; + drop(dummy_parachain_config); + + log::info!("start_solochain_node: is validator? {}", validator); - let relay_chain_slot_duration = Duration::from_secs(6); let overseer_handle = relay_chain_interface .overseer_handle() .map_err(|e| sc_service::Error::Application(Box::new(e)))?; - let sync_keystore = node_builder.keystore_container.keystore(); + let sync_keystore = keystore_container.keystore(); let collate_on_tanssi: Arc< dyn Fn() -> (CancellationToken, futures::channel::oneshot::Receiver<()>) + Send + Sync, > = Arc::new(move || { @@ -763,30 +880,6 @@ pub async fn start_solochain_node( panic!("Called collate_on_tanssi on solochain collator. This is unsupported and the runtime shouldn't allow this, it is a bug") }); - let announce_block = { - let sync_service = node_builder.network.sync_service.clone(); - Arc::new(move |hash, data| sync_service.announce_block(hash, data)) - }; - - let (mut node_builder, import_queue_service) = node_builder.extract_import_queue_service(); - - start_relay_chain_tasks(StartRelayChainTasksParams { - client: node_builder.client.clone(), - announce_block: announce_block.clone(), - para_id: orchestrator_para_id, - relay_chain_interface: relay_chain_interface.clone(), - task_manager: &mut node_builder.task_manager, - da_recovery_profile: if validator { - DARecoveryProfile::Collator - } else { - DARecoveryProfile::FullNode - }, - import_queue: import_queue_service, - relay_chain_slot_duration, - recovery_handle: Box::new(overseer_handle.clone()), - sync_service: node_builder.network.sync_service.clone(), - })?; - let orchestrator_chain_interface_builder = OrchestratorChainSolochainInterfaceBuilder { overseer_handle: overseer_handle.clone(), relay_chain_interface: relay_chain_interface.clone(), @@ -799,12 +892,10 @@ pub async fn start_solochain_node( orchestrator_chain_interface.clone(), sync_keystore.clone(), cc_spawn_tx.clone(), - node_builder.task_manager.spawn_essential_handle(), + task_manager.spawn_essential_handle(), ); } - let sync_keystore = node_builder.keystore_container.keystore(); - { let (container_chain_cli, tokio_handle) = container_chain_config; // If the orchestrator chain is running as a full-node, we start a full node for the @@ -823,9 +914,7 @@ pub async fn start_solochain_node( } // Start container chain spawner task. This will start and stop container chains on demand. - let orchestrator_client = node_builder.client.clone(); - let orchestrator_tx_pool = node_builder.transaction_pool.clone(); - let spawn_handle = node_builder.task_manager.spawn_handle(); + let spawn_handle = task_manager.spawn_handle(); let container_chain_spawner = ContainerChainSpawner { params: ContainerChainSpawnParams { @@ -840,8 +929,8 @@ pub async fn start_solochain_node( collation_params: if validator { Some(spawner::CollationParams { // TODO: all these args must be solochain instead of orchestrator - orchestrator_client: orchestrator_client.clone(), - orchestrator_tx_pool, + orchestrator_client: None, + orchestrator_tx_pool: None, orchestrator_para_id, collator_key: collator_key .expect("there should be a collator key if we're a validator"), @@ -872,20 +961,20 @@ pub async fn start_solochain_node( }; let state = container_chain_spawner.state.clone(); - node_builder.task_manager.spawn_essential_handle().spawn( + task_manager.spawn_essential_handle().spawn( "container-chain-spawner-rx-loop", None, container_chain_spawner.rx_loop(cc_spawn_rx, validator, true), ); - node_builder.task_manager.spawn_essential_handle().spawn( + task_manager.spawn_essential_handle().spawn( "container-chain-spawner-debug-state", None, monitor::monitor_task(state), ) } - Ok((node_builder.task_manager, node_builder.client)) + Ok((task_manager, ())) } pub const SOFT_DEADLINE_PERCENT: sp_runtime::Percent = sp_runtime::Percent::from_percent(100); @@ -1123,6 +1212,26 @@ impl OrchestratorChainSolochainInterfaceBuilder { } } +/// Builder for a concrete relay chain interface, created from a full node. Builds +/// a [`RelayChainInProcessInterface`] to access relay chain data necessary for parachain operation. +/// +/// The builder takes a [`polkadot_client::Client`] +/// that wraps a concrete instance. By using [`polkadot_client::ExecuteWithClient`] +/// the builder gets access to this concrete instance and instantiates a [`RelayChainInProcessInterface`] with it. +struct OrchestratorChainSolochainInterfaceBuilder { + overseer_handle: Handle, + relay_chain_interface: Arc, +} + +impl OrchestratorChainSolochainInterfaceBuilder { + pub fn build(self) -> Arc { + Arc::new(OrchestratorChainSolochainInterface::new( + self.overseer_handle, + self.relay_chain_interface, + )) + } +} + /// Provides an implementation of the [`RelayChainInterface`] using a local in-process relay chain node. pub struct OrchestratorChainInProcessInterface { pub full_client: Arc, diff --git a/test/configs/zombieStarlight.json b/test/configs/zombieStarlight.json index 9806c3057..10eedb68c 100644 --- a/test/configs/zombieStarlight.json +++ b/test/configs/zombieStarlight.json @@ -61,9 +61,8 @@ }, { "name": "Collator1000-01", - "command": "../target/release/tanssi-node", + "command": "../target/release/tanssi-node solo-chain", "args": [ - "--solo-chain", "--no-hardware-benchmarks", "--database=paritydb", "--wasmtime-precompiled=wasm" @@ -72,9 +71,8 @@ }, { "name": "Collator1000-02", - "command": "../target/release/tanssi-node", + "command": "../target/release/tanssi-node solo-chain", "args": [ - "--solo-chain", "--no-hardware-benchmarks", "--database=paritydb", "--wasmtime-precompiled=wasm" @@ -83,9 +81,8 @@ }, { "name": "Collator1000-03", - "command": "../target/release/tanssi-node", + "command": "../target/release/tanssi-node solo-chain", "args": [ - "--solo-chain", "--no-hardware-benchmarks", "--database=paritydb", "--wasmtime-precompiled=wasm" @@ -94,9 +91,8 @@ }, { "name": "Collator1000-04", - "command": "../target/release/tanssi-node", + "command": "../target/release/tanssi-node solo-chain", "args": [ - "--solo-chain", "--no-hardware-benchmarks", "--database=paritydb", "--wasmtime-precompiled=wasm" @@ -105,9 +101,8 @@ }, { "name": "Collator2000-01", - "command": "../target/release/tanssi-node", + "command": "../target/release/tanssi-node solo-chain", "args": [ - "--solo-chain", "--no-hardware-benchmarks", "--database=paritydb", "--wasmtime-precompiled=wasm" @@ -116,9 +111,8 @@ }, { "name": "Collator2000-02", - "command": "../target/release/tanssi-node", + "command": "../target/release/tanssi-node solo-chain", "args": [ - "--solo-chain", "--no-hardware-benchmarks", "--database=paritydb", "--wasmtime-precompiled=wasm" From 34c4310f10fe9d9aaa6a803c7df6ee7de788a930 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Tue, 17 Sep 2024 18:44:13 +0200 Subject: [PATCH 02/16] Refactor --- client/service-container-chain/src/cli.rs | 8 +- node/src/command/solochain.rs | 79 +++++++++++++++ node/src/service.rs | 117 ++++------------------ 3 files changed, 104 insertions(+), 100 deletions(-) diff --git a/client/service-container-chain/src/cli.rs b/client/service-container-chain/src/cli.rs index 57aa0f1d8..4c9ff727f 100644 --- a/client/service-container-chain/src/cli.rs +++ b/client/service-container-chain/src/cli.rs @@ -24,7 +24,7 @@ use { sc_chain_spec::ChainSpec, sc_network::config::MultiaddrWithPeerId, sp_runtime::Storage, - std::{collections::BTreeMap, net::SocketAddr, path::PathBuf}, + std::{collections::BTreeMap, net::SocketAddr}, }; /// The `run` command used to run a container chain node. @@ -97,6 +97,7 @@ impl ContainerChainRunCmd { }; let base_path = base_path.path().join("containers"); + new_base.base.shared_params.base_path = Some(base_path); ContainerChainCli { base: new_base, @@ -324,10 +325,7 @@ impl sc_cli::CliConfiguration for ContainerChainCli { } fn base_path(&self) -> sc_cli::Result> { - Ok(self - .shared_params() - .base_path()? - .or_else(|| Some(self.base_path.clone().into()))) + self.shared_params().base_path() } fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result> { diff --git a/node/src/command/solochain.rs b/node/src/command/solochain.rs index d309799c6..879a43e32 100644 --- a/node/src/command/solochain.rs +++ b/node/src/command/solochain.rs @@ -29,6 +29,7 @@ use sc_service::{BasePath, BlocksPruning, Configuration, DatabaseSource, TaskMan use sc_tracing::logging::LoggerBuilder; use std::future::Future; use std::num::NonZeroUsize; +use std::path::{Path, PathBuf}; use std::time::Duration; use tc_service_container_chain::cli::ContainerChainCli; @@ -330,3 +331,81 @@ pub fn dummy_config(tokio_handle: tokio::runtime::Handle, base_path: BasePath) - runtime_cache_size: 0, } } + +/// Returns the default path for configuration directory based on the chain_spec +pub(crate) fn build_solochain_config_dir(base_path: &PathBuf) -> PathBuf { + // Original: Collator1000-01/chains/dancebox/ + //base_path.path().join("chains").join(chain_id) + // Starlight: Collator1000-01/config/ + let mut base_path = base_path.clone(); + // Remove "/containers" + base_path.pop(); + base_path.join("config") +} + +/// Returns the default path for the network configuration inside the configuration dir +pub(crate) fn build_solochain_net_config_dir(config_dir: &PathBuf) -> PathBuf { + config_dir.join("network") +} + +/// Get the zombienet keystore path from the solochain collator keystore. +fn zombienet_keystore_path(keystore: &KeystoreConfig) -> PathBuf { + let keystore_path = keystore.path().unwrap(); + let mut zombienet_path = keystore_path.to_owned(); + // Collator1000-01/data/config/keystore/ + zombienet_path.pop(); + // Collator1000-01/data/config/ + zombienet_path.pop(); + // Collator1000-01/data/ + zombienet_path.push("chains/simple_container_2000/keystore/"); + // Collator1000-01/data/chains/simple_container_2000/keystore/ + + zombienet_path +} + +/// When running under zombienet, collator keys are injected in a different folder from what we +/// expect. This function will check if the zombienet folder exists, and if so, copy all the keys +/// from there into the expected folder. +pub fn copy_zombienet_keystore(keystore: &KeystoreConfig) { + // TODO: error handling? Or assume keystore_path always exists? + let keystore_path = keystore.path().unwrap(); + let zombienet_path = zombienet_keystore_path(keystore); + + if zombienet_path.exists() { + // Copy to keystore folder + + // https://stackoverflow.com/a/65192210 + // TODO: use a crate instead + // TODO: never overwrite files, only copy those that don't exist + fn copy_dir_all( + src: impl AsRef, + dst: impl AsRef, + files_copied: &mut u32, + ) -> std::io::Result<()> { + use std::fs; + fs::create_dir_all(&dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all( + entry.path(), + dst.as_ref().join(entry.file_name()), + files_copied, + )?; + } else { + fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + *files_copied += 1; + } + } + Ok(()) + } + + let mut files_copied = 0; + copy_dir_all(zombienet_path, keystore_path, &mut files_copied).unwrap(); + log::info!("Copied {} keys from zombienet keystore", files_copied); + } else { + // TODO: remove this log before merging + log::warn!("Copy nimbus keys to {:?}", keystore_path); + } +} diff --git a/node/src/service.rs b/node/src/service.rs index 1b3042e45..51b2be662 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -16,12 +16,15 @@ //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. -use crate::command::solochain::{dummy_config, NotParachainConfiguration}; +use crate::command::solochain::{ + build_solochain_config_dir, build_solochain_net_config_dir, copy_zombienet_keystore, + dummy_config, NotParachainConfiguration, +}; use sc_cli::CliConfiguration; use sc_network_common::role::Role; use sc_service::config::KeystoreConfig; use sc_service::KeystoreContainer; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use tc_consensus::collators::lookahead::BuyCoreParams; use { cumulus_client_cli::CollatorOptions, @@ -698,22 +701,6 @@ pub async fn start_parachain_node( .await } -/// Returns the default path for configuration directory based on the chain_spec -pub(crate) fn build_solochain_config_dir(base_path: &PathBuf) -> PathBuf { - // Original: Collator1000-01/chains/dancebox/ - //base_path.path().join("chains").join(chain_id) - // Starlight: Collator1000-01/config/ - let mut base_path = base_path.clone(); - // Remove "/containers" - base_path.pop(); - base_path.join("config") -} - -/// Returns the default path for the network configuration inside the configuration dir -pub(crate) fn build_solochain_net_config_dir(config_dir: &PathBuf) -> PathBuf { - config_dir.join("network") -} - fn keystore_config( keystore_params: Option<&sc_cli::KeystoreParams>, config_dir: &PathBuf, @@ -743,66 +730,27 @@ pub async fn start_solochain_node( let (cc_spawn_tx, cc_spawn_rx) = unbounded_channel(); let (telemetry_worker_handle, mut task_manager, keystore_container) = { - // TODO: instead of putting keystore in - // Collator1000-01/data/chains/simple_container_2000/keystore - // We want it in - // Collator1000-01/data/config/keystore - // And same for "network" folder - // But zombienet will put the keys in the old path, we need to manually copy it if we - // are running under zombienet - let config_dir = build_solochain_config_dir(&container_chain_config.0.base_path); + let base_path = container_chain_config + .0 + .base + .base + .shared_params + .base_path + .as_ref() + .expect("base_path is always set"); + let config_dir = build_solochain_config_dir(&base_path); let _net_config_dir = build_solochain_net_config_dir(&config_dir); let keystore = keystore_config(container_chain_config.0.keystore_params(), &config_dir).unwrap(); - { - let keystore_path = keystore.path().unwrap(); - let mut zombienet_path = keystore_path.to_owned(); - // Collator1000-01/data/config/keystore/ - zombienet_path.pop(); - // Collator1000-01/data/config/ - zombienet_path.pop(); - // Collator1000-01/data/ - zombienet_path.push("chains/simple_container_2000/keystore/"); - // Collator1000-01/data/chains/simple_container_2000/keystore/ - - if zombienet_path.exists() { - // Copy to keystore folder - - // https://stackoverflow.com/a/65192210 - // TODO: maybe use a crate instead - // TODO: never overwrite files, only copy those that don't exist - fn copy_dir_all( - src: impl AsRef, - dst: impl AsRef, - files_copied: &mut u32, - ) -> std::io::Result<()> { - use std::fs; - fs::create_dir_all(&dst)?; - for entry in fs::read_dir(src)? { - let entry = entry?; - let ty = entry.file_type()?; - if ty.is_dir() { - copy_dir_all( - entry.path(), - dst.as_ref().join(entry.file_name()), - files_copied, - )?; - } else { - fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; - *files_copied += 1; - } - } - Ok(()) - } - - let mut files_copied = 0; - copy_dir_all(zombienet_path, keystore_path, &mut files_copied).unwrap(); - log::info!("Copied {} keys from zombienet keystore", files_copied); - } else { - log::warn!("Copy nimbus keys to {:?}", keystore_path); - } - } + // Instead of putting keystore in + // Collator1000-01/data/chains/simple_container_2000/keystore + // We put it in + // Collator1000-01/data/config/keystore + // And same for "network" folder + // But zombienet will put the keys in the old path, so we need to manually copy it if we + // are running under zombienet + copy_zombienet_keystore(&keystore); let keystore_container = KeystoreContainer::new(&keystore)?; @@ -863,7 +811,6 @@ pub async fn start_solochain_node( ) .await .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - drop(dummy_parachain_config); log::info!("start_solochain_node: is validator? {}", validator); @@ -1212,26 +1159,6 @@ impl OrchestratorChainSolochainInterfaceBuilder { } } -/// Builder for a concrete relay chain interface, created from a full node. Builds -/// a [`RelayChainInProcessInterface`] to access relay chain data necessary for parachain operation. -/// -/// The builder takes a [`polkadot_client::Client`] -/// that wraps a concrete instance. By using [`polkadot_client::ExecuteWithClient`] -/// the builder gets access to this concrete instance and instantiates a [`RelayChainInProcessInterface`] with it. -struct OrchestratorChainSolochainInterfaceBuilder { - overseer_handle: Handle, - relay_chain_interface: Arc, -} - -impl OrchestratorChainSolochainInterfaceBuilder { - pub fn build(self) -> Arc { - Arc::new(OrchestratorChainSolochainInterface::new( - self.overseer_handle, - self.relay_chain_interface, - )) - } -} - /// Provides an implementation of the [`RelayChainInterface`] using a local in-process relay chain node. pub struct OrchestratorChainInProcessInterface { pub full_client: Arc, From ffb7bcf9e9a9809253589aa40524af08efe3caa5 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Thu, 19 Sep 2024 14:07:19 +0200 Subject: [PATCH 03/16] Refactors --- client/consensus/src/collators/lookahead.rs | 9 ----- node/src/command.rs | 14 +------- node/src/command/solochain.rs | 37 ++++++++++----------- node/src/service.rs | 14 ++++---- 4 files changed, 25 insertions(+), 49 deletions(-) diff --git a/client/consensus/src/collators/lookahead.rs b/client/consensus/src/collators/lookahead.rs index bb0f06ace..089978179 100644 --- a/client/consensus/src/collators/lookahead.rs +++ b/client/consensus/src/collators/lookahead.rs @@ -371,15 +371,6 @@ impl Clone for BuyCoreParams { } } -impl BuyCoreParams { - pub fn is_solochain(&self) -> bool { - match self { - BuyCoreParams::Solochain { .. } => true, - _ => false, - } - } -} - /// Run async-backing-friendly for Tanssi Aura. pub fn run< GSD, diff --git a/node/src/command.rs b/node/src/command.rs index 76907d5a6..419fcefec 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see . -use crate::command::solochain::NotParachainConfiguration; use { crate::{ chain_spec, @@ -373,12 +372,10 @@ pub fn run() -> Result<()> { let relay_chain_args = binding.iter().chain(cmd.relay_chain_args.iter()); let polkadot_cli = { let base_path = config.base_path.path().join("polkadot"); - // TODO: where to get chain_id from? - let chain_id = Some("starlight_local_testnet".to_string()); RelayChainCli { base_path, - chain_id, + chain_id: Some(config.relay_chain.clone()), base: clap::Parser::parse_from(relay_chain_args), } }; @@ -405,26 +402,17 @@ pub fn run() -> Result<()> { let container_chain_cli = normalized_run; let tokio_handle = config.tokio_handle.clone(); let container_chain_config = (container_chain_cli, tokio_handle); - let not_config = NotParachainConfiguration { - chain_type: polkadot_config.chain_spec.chain_type(), - // relay_chain will be set to "starlight_local_testnet" - // But everywhere else it is hardcoded to "rococo-local" so not sure if it's used - relay_chain: polkadot_config.chain_spec.id().to_string(), - collator: container_chain_config.0.base.collator, - }; // TODO: we can't enable hwbench because we don't have a db. Find a workaround let hwbench = None; crate::service::start_solochain_node( - not_config, polkadot_config, container_chain_config, collator_options, hwbench, ) .await - .map(|r| r.0) .map_err(Into::into) }) } diff --git a/node/src/command/solochain.rs b/node/src/command/solochain.rs index 879a43e32..19ea16a33 100644 --- a/node/src/command/solochain.rs +++ b/node/src/command/solochain.rs @@ -33,21 +33,23 @@ use std::path::{Path, PathBuf}; use std::time::Duration; use tc_service_container_chain::cli::ContainerChainCli; -pub struct SlimConfig { +/// Alternative to [Configuration] struct used in solochain context. +pub struct SolochainConfig { pub tokio_handle: tokio::runtime::Handle, pub base_path: BasePath, pub network_node_name: String, - pub display_role: String, + pub role: Role, + pub relay_chain: String, } -/// A Substrate CLI runtime that can be used to run a node or a command -pub struct NotRunner { - config: SlimConfig, +/// Alternative to [Runner](sc_cli::Runner) struct used in solochain context. +pub struct SolochainRunner { + config: SolochainConfig, tokio_runtime: tokio::runtime::Runtime, signals: Signals, } -impl NotRunner { +impl SolochainRunner { /// Log information about the node itself. /// /// # Example: @@ -76,7 +78,7 @@ impl NotRunner { ); //info!("📋 Chain specification: {}", config.chain_spec.name()); info!("🏷 Node name: {}", self.config.network_node_name); - info!("👤 Role: {}", self.config.display_role); + info!("👤 Role: {}", self.config.role); /* info!( "💾 Database: {} at {}", @@ -100,7 +102,7 @@ impl NotRunner { /// `SIGTERM` or `SIGINT`. pub fn run_node_until_exit( self, - initialize: impl FnOnce(SlimConfig) -> F, + initialize: impl FnOnce(SolochainConfig) -> F, ) -> std::result::Result<(), E> where F: Future>, @@ -161,7 +163,7 @@ impl NotRunner { pub fn create_runner, DVC: DefaultConfigurationValues>( cli: &Cli, command: &T, -) -> sc_cli::Result { +) -> sc_cli::Result { let tokio_runtime = sc_cli::build_runtime()?; // `capture` needs to be called in a tokio context. @@ -177,14 +179,18 @@ pub fn create_runner, DVC: DefaultConfigurationValues>( } else { Role::Full }; - let config = SlimConfig { + // TODO: where to get chain_id from? + let chain_id = "starlight_local_testnet".to_string(); + + let config = SolochainConfig { tokio_handle: tokio_runtime.handle().clone(), base_path, network_node_name, - display_role: role.to_string(), + role, + relay_chain: chain_id, }; - Ok(NotRunner { + Ok(SolochainRunner { config, tokio_runtime, signals, @@ -243,13 +249,6 @@ fn init_cmd, DVC: DefaultConfigurationValues>( Ok(()) } -// TODO: merge this with SlimConfig -pub struct NotParachainConfiguration { - pub chain_type: sc_chain_spec::ChainType, - pub relay_chain: String, - pub collator: bool, -} - /// Create a dummy [Configuration] that should only be used as input to polkadot-sdk functions that /// take this struct as input but only use one field of it. /// This is needed because [Configuration] does not implement [Default]. diff --git a/node/src/service.rs b/node/src/service.rs index f681a6de8..c8627a194 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -18,7 +18,7 @@ use crate::command::solochain::{ build_solochain_config_dir, build_solochain_net_config_dir, copy_zombienet_keystore, - dummy_config, NotParachainConfiguration, + dummy_config, }; use sc_cli::CliConfiguration; use sc_network_common::role::Role; @@ -704,18 +704,16 @@ fn keystore_config( /// Start a solochain node. pub async fn start_solochain_node( // Parachain config not used directly, but we need it to derive the default values for some container_config args - orchestrator_config: NotParachainConfiguration, polkadot_config: Configuration, container_chain_config: (ContainerChainCli, tokio::runtime::Handle), collator_options: CollatorOptions, hwbench: Option, -) -> sc_service::error::Result<(TaskManager, ())> { +) -> sc_service::error::Result { let orchestrator_para_id = Default::default(); //orchestrator_config.announce_block = false; // prepare_node_config() - let parachain_config = orchestrator_config; - let chain_type: sc_chain_spec::ChainType = parachain_config.chain_type.clone(); - let relay_chain = parachain_config.relay_chain.clone(); + let chain_type = polkadot_config.chain_spec.chain_type().clone(); + let relay_chain = polkadot_config.chain_spec.id().to_string(); // Channel to send messages to start/stop container chains let (cc_spawn_tx, cc_spawn_rx) = unbounded_channel(); @@ -781,7 +779,7 @@ pub async fn start_solochain_node( // Dummy parachain config only needed because `build_relay_chain_interface` needs to know if we // are collators or not - let validator = parachain_config.collator; + let validator = container_chain_config.0.base.collator; let mut dummy_parachain_config = dummy_config( polkadot_config.tokio_handle.clone(), polkadot_config.base_path.clone(), @@ -899,7 +897,7 @@ pub async fn start_solochain_node( ) } - Ok((task_manager, ())) + Ok(task_manager) } pub const SOFT_DEADLINE_PERCENT: sp_runtime::Percent = sp_runtime::Percent::from_percent(100); From cd9e27efb9a3356ba91f2d3dba9f632ca4fdea19 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Thu, 19 Sep 2024 14:08:50 +0200 Subject: [PATCH 04/16] Remove cli test --- node/src/cli.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/node/src/cli.rs b/node/src/cli.rs index b9efc4b14..ebb1d8c07 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -201,7 +201,7 @@ impl KeyCmd { #[derive(Debug, clap::Parser)] #[command( - propagate_version = false, // setting this to true makes the verify_cli test fail + propagate_version = true, args_conflicts_with_subcommands = true, subcommand_negates_reqs = true )] @@ -286,9 +286,3 @@ impl RelayChainCli { } } } - -#[test] -fn verify_cli() { - use clap::CommandFactory; - Cli::command().debug_assert(); -} From 38b528ffea1955d0e2e4b4d25529a9ac127bd295 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Thu, 19 Sep 2024 18:25:11 +0200 Subject: [PATCH 05/16] Cleanup --- client/consensus/src/mocks.rs | 17 +-- client/service-container-chain/src/cli.rs | 4 +- client/service-container-chain/src/service.rs | 3 +- node/src/cli.rs | 13 +- node/src/command.rs | 42 +----- node/src/command/solochain.rs | 87 ++++++----- node/src/service.rs | 136 +++++++++--------- 7 files changed, 133 insertions(+), 169 deletions(-) diff --git a/client/consensus/src/mocks.rs b/client/consensus/src/mocks.rs index 9cd96a45b..13f2955ed 100644 --- a/client/consensus/src/mocks.rs +++ b/client/consensus/src/mocks.rs @@ -15,6 +15,7 @@ // along with Tanssi. If not, see . use { + crate::collators::lookahead::BuyCoreParams, crate::{ collators::lookahead::Params as LookAheadParams, OrchestratorAuraWorkerAuxData, SlotFrequency, @@ -23,7 +24,10 @@ use { cumulus_client_collator::service::CollatorService, cumulus_client_consensus_common::{ParachainBlockImportMarker, ValidationCodeHashProvider}, cumulus_client_consensus_proposer::Proposer as ConsensusProposer, - cumulus_primitives_core::{relay_chain::BlockId, CollationInfo, CollectCollationInfo, ParaId}, + cumulus_primitives_core::{ + relay_chain::{BlockId, ValidationCodeHash}, + CollationInfo, CollectCollationInfo, ParaId, + }, cumulus_relay_chain_interface::{ CommittedCandidateReceipt, OverseerHandle, RelayChainInterface, RelayChainResult, StorageValue, @@ -35,7 +39,10 @@ use { pallet_xcm_core_buyer_runtime_api::BuyingError, parity_scale_codec::Encode, polkadot_core_primitives::{Header as PHeader, InboundDownwardMessage, InboundHrmpMessage}, - polkadot_node_subsystem::messages::{RuntimeApiMessage, RuntimeApiRequest}, + polkadot_node_subsystem::{ + messages::{RuntimeApiMessage, RuntimeApiRequest}, + overseer, OverseerSignal, + }, polkadot_overseer::dummy::dummy_overseer_builder, polkadot_parachain_primitives::primitives::HeadData, polkadot_primitives::{ @@ -512,12 +519,6 @@ impl sc_consensus::Verifier for SealExtractorVerfier { } } -use crate::collators::lookahead::BuyCoreParams; -use { - cumulus_primitives_core::relay_chain::ValidationCodeHash, - polkadot_node_subsystem::{overseer, OverseerSignal}, -}; - pub struct DummyCodeHashProvider; impl ValidationCodeHashProvider for DummyCodeHashProvider { fn code_hash_at(&self, _at: PHash) -> Option { diff --git a/client/service-container-chain/src/cli.rs b/client/service-container-chain/src/cli.rs index 4c9ff727f..79bd41a89 100644 --- a/client/service-container-chain/src/cli.rs +++ b/client/service-container-chain/src/cli.rs @@ -14,17 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see -use sc_cli::CliConfiguration; -use url::Url; use { crate::chain_spec::RawGenesisConfig, cumulus_client_cli::{CollatorOptions, RelayChainMode}, dc_orchestrator_chain_interface::ContainerChainGenesisData, dp_container_chain_genesis_data::json::properties_to_map, sc_chain_spec::ChainSpec, + sc_cli::CliConfiguration, sc_network::config::MultiaddrWithPeerId, sp_runtime::Storage, std::{collections::BTreeMap, net::SocketAddr}, + url::Url, }; /// The `run` command used to run a container chain node. diff --git a/client/service-container-chain/src/service.rs b/client/service-container-chain/src/service.rs index 07111fc23..d3c8dfa9f 100644 --- a/client/service-container-chain/src/service.rs +++ b/client/service-container-chain/src/service.rs @@ -52,7 +52,7 @@ use { substrate_prometheus_endpoint::Registry, tc_consensus::{ collators::lookahead::{ - self as lookahead_tanssi_aura, Params as LookaheadTanssiAuraParams, + self as lookahead_tanssi_aura, BuyCoreParams, Params as LookaheadTanssiAuraParams, }, OrchestratorAuraWorkerAuxData, }, @@ -61,7 +61,6 @@ use { #[allow(deprecated)] use sc_executor::NativeElseWasmExecutor; -use tc_consensus::collators::lookahead::BuyCoreParams; type FullBackend = TFullBackend; diff --git a/node/src/cli.rs b/node/src/cli.rs index ebb1d8c07..9208a3b3f 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -14,11 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see . -use tc_service_container_chain::cli::ContainerChainRunCmd; use { node_common::service::Sealing, sc_cli::{CliConfiguration, NodeKeyParams, SharedParams}, std::path::PathBuf, + tc_service_container_chain::cli::ContainerChainRunCmd, }; /// Sub-commands supported by the collator. @@ -86,17 +86,6 @@ pub struct SoloChainCmd { #[arg(long)] pub no_hardware_benchmarks: bool, - /* - /// Enable the development service to run without a backing relay chain - #[arg(long)] - pub dev_service: bool, - - /// When blocks should be sealed in the dev service. - /// - /// Options are "instant", "manual", or timer interval in milliseconds - #[arg(long, default_value = "instant")] - pub sealing: Sealing, - */ /// Relay chain arguments #[arg(raw = true)] pub relay_chain_args: Vec, diff --git a/node/src/command.rs b/node/src/command.rs index 419fcefec..50f287ec6 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see . +use crate::command::solochain::relay_chain_cli_new; use { crate::{ chain_spec, @@ -343,8 +344,8 @@ pub fn run() -> Result<()> { Some(Subcommand::SoloChain(cmd)) => { // Cannot use create_configuration function because that needs a chain spec. // So write our own `create_runner` function that doesn't need chain spec. - let normalized_run = cmd.run.normalize(); - let runner = solochain::create_runner(&cli, &normalized_run)?; + let container_chain_cli = cmd.run.normalize(); + let runner = solochain::create_runner(&cli, &container_chain_cli)?; // TODO: Assert that there are no flags between `tanssi-node` and `solo-chain`. // These will be ignored anyway. @@ -359,56 +360,23 @@ pub fn run() -> Result<()> { let collator_options = cmd.run.collator_options(); runner.run_node_until_exit(|config| async move { - /* - let polkadot_cli = RelayChainCli::new( + let polkadot_cli = relay_chain_cli_new( &config, [RelayChainCli::executable_name()] .iter() .chain(cmd.relay_chain_args.iter()), ); - */ - // TODO: refactor this into function that returns `RelayChainCli` - let binding = [RelayChainCli::executable_name()]; - let relay_chain_args = binding.iter().chain(cmd.relay_chain_args.iter()); - let polkadot_cli = { - let base_path = config.base_path.path().join("polkadot"); - - RelayChainCli { - base_path, - chain_id: Some(config.relay_chain.clone()), - base: clap::Parser::parse_from(relay_chain_args), - } - }; - - // TODO: dev mode does not make sense for starlight collator? - // But better to detect --dev flag and panic than ignore it - /* - let dev_service = config.chain_spec.is_dev() - || relay_chain_id == Some("dev-service".to_string()) - || cli_run_dev_service; - - if dev_service { - return crate::service::start_dev_node(config, cli_run_sealing, hwbench, id) - .map_err(Into::into); - } - */ - let tokio_handle = config.tokio_handle.clone(); let polkadot_config = SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) .map_err(|err| format!("Relay chain argument error: {}", err))?; - // We need to bake in some container-chain args - let container_chain_cli = normalized_run; - let tokio_handle = config.tokio_handle.clone(); - let container_chain_config = (container_chain_cli, tokio_handle); - // TODO: we can't enable hwbench because we don't have a db. Find a workaround let hwbench = None; crate::service::start_solochain_node( polkadot_config, - container_chain_config, + container_chain_cli, collator_options, hwbench, ) diff --git a/node/src/command/solochain.rs b/node/src/command/solochain.rs index 19ea16a33..1a56e7ea6 100644 --- a/node/src/command/solochain.rs +++ b/node/src/command/solochain.rs @@ -16,7 +16,7 @@ //! Helper functions used to implement solochain collator -use crate::cli::Cli; +use crate::cli::{Cli, RelayChainCli}; use futures::FutureExt; use jsonrpsee::server::BatchRequestConfig; use log::{info, warn}; @@ -249,6 +249,20 @@ fn init_cmd, DVC: DefaultConfigurationValues>( Ok(()) } +/// Equivalent to [RelayChainCli::new] +pub fn relay_chain_cli_new<'a>( + config: &SolochainConfig, + relay_chain_args: impl Iterator, +) -> RelayChainCli { + let base_path = config.base_path.path().join("polkadot"); + + RelayChainCli { + base_path, + chain_id: Some(config.relay_chain.clone()), + base: clap::Parser::parse_from(relay_chain_args), + } +} + /// Create a dummy [Configuration] that should only be used as input to polkadot-sdk functions that /// take this struct as input but only use one field of it. /// This is needed because [Configuration] does not implement [Default]. @@ -333,12 +347,11 @@ pub fn dummy_config(tokio_handle: tokio::runtime::Handle, base_path: BasePath) - /// Returns the default path for configuration directory based on the chain_spec pub(crate) fn build_solochain_config_dir(base_path: &PathBuf) -> PathBuf { - // Original: Collator1000-01/chains/dancebox/ - //base_path.path().join("chains").join(chain_id) - // Starlight: Collator1000-01/config/ + // base_path: Collator1000-01/data/containers + // config_dir: Collator1000-01/data/config let mut base_path = base_path.clone(); - // Remove "/containers" base_path.pop(); + base_path.join("config") } @@ -365,46 +378,44 @@ fn zombienet_keystore_path(keystore: &KeystoreConfig) -> PathBuf { /// When running under zombienet, collator keys are injected in a different folder from what we /// expect. This function will check if the zombienet folder exists, and if so, copy all the keys /// from there into the expected folder. -pub fn copy_zombienet_keystore(keystore: &KeystoreConfig) { - // TODO: error handling? Or assume keystore_path always exists? +pub fn copy_zombienet_keystore(keystore: &KeystoreConfig) -> std::io::Result<()> { let keystore_path = keystore.path().unwrap(); let zombienet_path = zombienet_keystore_path(keystore); if zombienet_path.exists() { // Copy to keystore folder - - // https://stackoverflow.com/a/65192210 - // TODO: use a crate instead - // TODO: never overwrite files, only copy those that don't exist - fn copy_dir_all( - src: impl AsRef, - dst: impl AsRef, - files_copied: &mut u32, - ) -> std::io::Result<()> { - use std::fs; - fs::create_dir_all(&dst)?; - for entry in fs::read_dir(src)? { - let entry = entry?; - let ty = entry.file_type()?; - if ty.is_dir() { - copy_dir_all( - entry.path(), - dst.as_ref().join(entry.file_name()), - files_copied, - )?; - } else { - fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; - *files_copied += 1; - } - } - Ok(()) - } - let mut files_copied = 0; - copy_dir_all(zombienet_path, keystore_path, &mut files_copied).unwrap(); + copy_dir_all(zombienet_path, keystore_path, &mut files_copied)?; log::info!("Copied {} keys from zombienet keystore", files_copied); + + Ok(()) } else { - // TODO: remove this log before merging - log::warn!("Copy nimbus keys to {:?}", keystore_path); + // Zombienet folder does not exist, assume we are not running under zombienet + Ok(()) } } + +// https://stackoverflow.com/a/65192210 +fn copy_dir_all( + src: impl AsRef, + dst: impl AsRef, + files_copied: &mut u32, +) -> std::io::Result<()> { + use std::fs; + fs::create_dir_all(&dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all( + entry.path(), + dst.as_ref().join(entry.file_name()), + files_copied, + )?; + } else { + fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + *files_copied += 1; + } + } + Ok(()) +} diff --git a/node/src/service.rs b/node/src/service.rs index c8627a194..a5aeaf982 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -705,22 +705,18 @@ fn keystore_config( pub async fn start_solochain_node( // Parachain config not used directly, but we need it to derive the default values for some container_config args polkadot_config: Configuration, - container_chain_config: (ContainerChainCli, tokio::runtime::Handle), + container_chain_cli: ContainerChainCli, collator_options: CollatorOptions, hwbench: Option, ) -> sc_service::error::Result { + let tokio_handle = polkadot_config.tokio_handle.clone(); let orchestrator_para_id = Default::default(); - //orchestrator_config.announce_block = false; // prepare_node_config() let chain_type = polkadot_config.chain_spec.chain_type().clone(); let relay_chain = polkadot_config.chain_spec.id().to_string(); - // Channel to send messages to start/stop container chains - let (cc_spawn_tx, cc_spawn_rx) = unbounded_channel(); - let (telemetry_worker_handle, mut task_manager, keystore_container) = { - let base_path = container_chain_config - .0 + let base_path = container_chain_cli .base .base .shared_params @@ -729,8 +725,8 @@ pub async fn start_solochain_node( .expect("base_path is always set"); let config_dir = build_solochain_config_dir(&base_path); let _net_config_dir = build_solochain_net_config_dir(&config_dir); - let keystore = - keystore_config(container_chain_config.0.keystore_params(), &config_dir).unwrap(); + let keystore = keystore_config(container_chain_cli.keystore_params(), &config_dir) + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; // Instead of putting keystore in // Collator1000-01/data/chains/simple_container_2000/keystore @@ -739,16 +735,17 @@ pub async fn start_solochain_node( // And same for "network" folder // But zombienet will put the keys in the old path, so we need to manually copy it if we // are running under zombienet - copy_zombienet_keystore(&keystore); + copy_zombienet_keystore(&keystore)?; let keystore_container = KeystoreContainer::new(&keystore)?; let task_manager = { //let registry = config.prometheus_config.as_ref().map(|cfg| &cfg.registry); let registry = None; - TaskManager::new(container_chain_config.1.clone(), registry)? + TaskManager::new(tokio_handle.clone(), registry)? }; + // TODO: impl telemetry /* let telemetry = parachain_config .telemetry_endpoints @@ -779,7 +776,7 @@ pub async fn start_solochain_node( // Dummy parachain config only needed because `build_relay_chain_interface` needs to know if we // are collators or not - let validator = container_chain_config.0.base.collator; + let validator = container_chain_cli.base.collator; let mut dummy_parachain_config = dummy_config( polkadot_config.tokio_handle.clone(), polkadot_config.base_path.clone(), @@ -821,6 +818,8 @@ pub async fn start_solochain_node( relay_chain_interface: relay_chain_interface.clone(), }; let orchestrator_chain_interface = orchestrator_chain_interface_builder.build(); + // Channel to send messages to start/stop container chains + let (cc_spawn_tx, cc_spawn_rx) = unbounded_channel(); if validator { // Start task which detects para id assignment, and starts/stops container chains. @@ -832,70 +831,67 @@ pub async fn start_solochain_node( ); } - { - let (container_chain_cli, tokio_handle) = container_chain_config; - // If the orchestrator chain is running as a full-node, we start a full node for the - // container chain immediately, because only collator nodes detect their container chain - // assignment so otherwise it will never start. - if !validator { - if let Some(container_chain_para_id) = container_chain_cli.base.para_id { - // Spawn new container chain node - cc_spawn_tx - .send(CcSpawnMsg::UpdateAssignment { - current: Some(container_chain_para_id.into()), - next: Some(container_chain_para_id.into()), - }) - .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - } + // If the orchestrator chain is running as a full-node, we start a full node for the + // container chain immediately, because only collator nodes detect their container chain + // assignment so otherwise it will never start. + if !validator { + if let Some(container_chain_para_id) = container_chain_cli.base.para_id { + // Spawn new container chain node + cc_spawn_tx + .send(CcSpawnMsg::UpdateAssignment { + current: Some(container_chain_para_id.into()), + next: Some(container_chain_para_id.into()), + }) + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; } + } - // Start container chain spawner task. This will start and stop container chains on demand. - let spawn_handle = task_manager.spawn_handle(); + // Start container chain spawner task. This will start and stop container chains on demand. + let spawn_handle = task_manager.spawn_handle(); - let container_chain_spawner = ContainerChainSpawner { - params: ContainerChainSpawnParams { - orchestrator_chain_interface, - container_chain_cli, - tokio_handle, - chain_type, - relay_chain, - relay_chain_interface, - sync_keystore, - orchestrator_para_id, - collation_params: if validator { - Some(spawner::CollationParams { - // TODO: all these args must be solochain instead of orchestrator - orchestrator_client: None, - orchestrator_tx_pool: None, - orchestrator_para_id, - collator_key: collator_key - .expect("there should be a collator key if we're a validator"), - solochain: true, - }) - } else { - None - }, - spawn_handle, - data_preserver: false, + let container_chain_spawner = ContainerChainSpawner { + params: ContainerChainSpawnParams { + orchestrator_chain_interface, + container_chain_cli, + tokio_handle, + chain_type, + relay_chain, + relay_chain_interface, + sync_keystore, + orchestrator_para_id, + collation_params: if validator { + Some(spawner::CollationParams { + // TODO: all these args must be solochain instead of orchestrator + orchestrator_client: None, + orchestrator_tx_pool: None, + orchestrator_para_id, + collator_key: collator_key + .expect("there should be a collator key if we're a validator"), + solochain: true, + }) + } else { + None }, - state: Default::default(), - collate_on_tanssi, - collation_cancellation_constructs: None, - }; - let state = container_chain_spawner.state.clone(); + spawn_handle, + data_preserver: false, + }, + state: Default::default(), + collate_on_tanssi, + collation_cancellation_constructs: None, + }; + let state = container_chain_spawner.state.clone(); - task_manager.spawn_essential_handle().spawn( - "container-chain-spawner-rx-loop", - None, - container_chain_spawner.rx_loop(cc_spawn_rx, validator, true), - ); + task_manager.spawn_essential_handle().spawn( + "container-chain-spawner-rx-loop", + None, + container_chain_spawner.rx_loop(cc_spawn_rx, validator, true), + ); - task_manager.spawn_essential_handle().spawn( - "container-chain-spawner-debug-state", - None, - monitor::monitor_task(state), - ) - } + task_manager.spawn_essential_handle().spawn( + "container-chain-spawner-debug-state", + None, + monitor::monitor_task(state), + ); Ok(task_manager) } From f76b4734722bd16116fe7aaac181e0c9415f6f8f Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Fri, 20 Sep 2024 10:50:50 +0200 Subject: [PATCH 06/16] Fix role --- client/consensus/src/mocks.rs | 5 ++--- node/src/cli.rs | 4 ---- node/src/command.rs | 39 ++++++++++++++++++++++++++++------- node/src/command/solochain.rs | 14 +++++-------- node/src/service.rs | 1 - 5 files changed, 39 insertions(+), 24 deletions(-) diff --git a/client/consensus/src/mocks.rs b/client/consensus/src/mocks.rs index 13f2955ed..1ec7358dd 100644 --- a/client/consensus/src/mocks.rs +++ b/client/consensus/src/mocks.rs @@ -15,10 +15,9 @@ // along with Tanssi. If not, see . use { - crate::collators::lookahead::BuyCoreParams, crate::{ - collators::lookahead::Params as LookAheadParams, OrchestratorAuraWorkerAuxData, - SlotFrequency, + collators::lookahead::{BuyCoreParams, Params as LookAheadParams}, + OrchestratorAuraWorkerAuxData, SlotFrequency, }, async_trait::async_trait, cumulus_client_collator::service::CollatorService, diff --git a/node/src/cli.rs b/node/src/cli.rs index 9208a3b3f..a01edcfb4 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -150,10 +150,6 @@ pub struct RunCmd { #[arg(long)] pub dev_service: bool, - /// Enable collators to run against a solo-chain such as Starlight - #[arg(long)] - pub solo_chain: bool, - /// When blocks should be sealed in the dev service. /// /// Options are "instant", "manual", or timer interval in milliseconds diff --git a/node/src/command.rs b/node/src/command.rs index 50f287ec6..0cf84b2d0 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -345,12 +345,17 @@ pub fn run() -> Result<()> { // Cannot use create_configuration function because that needs a chain spec. // So write our own `create_runner` function that doesn't need chain spec. let container_chain_cli = cmd.run.normalize(); - let runner = solochain::create_runner(&cli, &container_chain_cli)?; + let runner = solochain::create_runner(&container_chain_cli)?; - // TODO: Assert that there are no flags between `tanssi-node` and `solo-chain`. - // These will be ignored anyway. - // We need to do this after create_runner because before that logging is not setup yet - // Zombienet appends a --chain flag after "solo-chain" subcommand, which is ignored, + // The expected usage is + // `tanssi-node solochain --flag` + // So `cmd` stores the flags from after `solochain`, and `cli` has the flags from between + // `tanssi-node` and `solo-chain`. We are ignoring the flags from `cli` intentionally. + // Would be nice to error if the user passes any flag there, but it's not easy to detect. + + // Zombienet appends a --chain flag after "solo-chain" subcommand, which is ignored, so it's fine, + // but warn users that this is not expected here. + // We cannot do this before create_runner because logging is not setup there yet. if cmd.run.base.shared_params.chain.is_some() { log::warn!( "Ignoring --chain argument: solochain mode does only need the relay chain-spec" @@ -360,6 +365,20 @@ pub fn run() -> Result<()> { let collator_options = cmd.run.collator_options(); runner.run_node_until_exit(|config| async move { + let containers_base_path = container_chain_cli + .base + .base + .shared_params + .base_path + .as_ref() + .expect("base_path is always set"); + let hwbench = (!cmd.no_hardware_benchmarks) + .then_some(Some(containers_base_path).map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench(Some(database_path)) + })) + .flatten(); + let polkadot_cli = relay_chain_cli_new( &config, [RelayChainCli::executable_name()] @@ -371,8 +390,14 @@ pub fn run() -> Result<()> { SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) .map_err(|err| format!("Relay chain argument error: {}", err))?; - // TODO: we can't enable hwbench because we don't have a db. Find a workaround - let hwbench = None; + info!( + "Is collating: {}", + if config.role.is_authority() { + "yes" + } else { + "no" + } + ); crate::service::start_solochain_node( polkadot_config, diff --git a/node/src/command/solochain.rs b/node/src/command/solochain.rs index 1a56e7ea6..dd7d9dcae 100644 --- a/node/src/command/solochain.rs +++ b/node/src/command/solochain.rs @@ -161,7 +161,6 @@ impl SolochainRunner { /// Equivalent to [Cli::create_runner] pub fn create_runner, DVC: DefaultConfigurationValues>( - cli: &Cli, command: &T, ) -> sc_cli::Result { let tokio_runtime = sc_cli::build_runtime()?; @@ -174,20 +173,17 @@ pub fn create_runner, DVC: DefaultConfigurationValues>( let base_path = command.base_path()?.unwrap(); let network_node_name = command.node_name()?; - let role = if cli.run.collator { - Role::Authority - } else { - Role::Full - }; - // TODO: where to get chain_id from? - let chain_id = "starlight_local_testnet".to_string(); + let is_dev = command.is_dev()?; + let role = command.role(is_dev)?; + // TODO: where to get relay_chain_id from? + let relay_chain_id = "starlight_local_testnet".to_string(); let config = SolochainConfig { tokio_handle: tokio_runtime.handle().clone(), base_path, network_node_name, role, - relay_chain: chain_id, + relay_chain: relay_chain_id, }; Ok(SolochainRunner { diff --git a/node/src/service.rs b/node/src/service.rs index a5aeaf982..a2b8c402c 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -703,7 +703,6 @@ fn keystore_config( /// Start a solochain node. pub async fn start_solochain_node( - // Parachain config not used directly, but we need it to derive the default values for some container_config args polkadot_config: Configuration, container_chain_cli: ContainerChainCli, collator_options: CollatorOptions, From cc1c9dd9fcb187ad8d3286683a25b9beaae09324 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Fri, 20 Sep 2024 11:11:17 +0200 Subject: [PATCH 07/16] Refactor --- client/service-container-chain/src/spawner.rs | 1 + node/src/command.rs | 3 +- node/src/command/solochain.rs | 15 +-- node/src/service.rs | 104 +++++++----------- 4 files changed, 43 insertions(+), 80 deletions(-) diff --git a/client/service-container-chain/src/spawner.rs b/client/service-container-chain/src/spawner.rs index 4fcb8dfb0..76a809a9e 100644 --- a/client/service-container-chain/src/spawner.rs +++ b/client/service-container-chain/src/spawner.rs @@ -118,6 +118,7 @@ pub struct CollationParams { pub orchestrator_tx_pool: Option>>, pub orchestrator_client: Option>, pub orchestrator_para_id: ParaId, + /// If this is `false`, then `orchestrator_tx_pool` and `orchestrator_client` must be `Some`. pub solochain: bool, } diff --git a/node/src/command.rs b/node/src/command.rs index 0cf84b2d0..e159b2ce2 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Tanssi. If not, see . -use crate::command::solochain::relay_chain_cli_new; use { crate::{ chain_spec, @@ -379,7 +378,7 @@ pub fn run() -> Result<()> { })) .flatten(); - let polkadot_cli = relay_chain_cli_new( + let polkadot_cli = solochain::relay_chain_cli_new( &config, [RelayChainCli::executable_name()] .iter() diff --git a/node/src/command/solochain.rs b/node/src/command/solochain.rs index dd7d9dcae..0a19459ac 100644 --- a/node/src/command/solochain.rs +++ b/node/src/command/solochain.rs @@ -76,21 +76,13 @@ impl SolochainRunner { C::copyright_start_year(), Local::now().year() ); + // No chain spec //info!("📋 Chain specification: {}", config.chain_spec.name()); info!("🏷 Node name: {}", self.config.network_node_name); info!("👤 Role: {}", self.config.role); - /* - info!( - "💾 Database: {} at {}", - config.database, - config - .database - .path() - .map_or_else(|| "".to_owned(), |p| p.display().to_string()) - ); - */ info!( "💾 Database: {} at {}", + // Container chains only support paritydb "ParityDb", // Print base path instead of db path because each container will have its own db in a // different subdirectory. @@ -341,7 +333,7 @@ pub fn dummy_config(tokio_handle: tokio::runtime::Handle, base_path: BasePath) - } } -/// Returns the default path for configuration directory based on the chain_spec +/// Returns the default path for configuration directory based on the chain_spec pub(crate) fn build_solochain_config_dir(base_path: &PathBuf) -> PathBuf { // base_path: Collator1000-01/data/containers // config_dir: Collator1000-01/data/config @@ -391,6 +383,7 @@ pub fn copy_zombienet_keystore(keystore: &KeystoreConfig) -> std::io::Result<()> } } +/// Equivalent to `cp -r src/* dst` // https://stackoverflow.com/a/65192210 fn copy_dir_all( src: impl AsRef, diff --git a/node/src/service.rs b/node/src/service.rs index a2b8c402c..2aebabe9e 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -16,17 +16,11 @@ //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. -use crate::command::solochain::{ - build_solochain_config_dir, build_solochain_net_config_dir, copy_zombienet_keystore, - dummy_config, -}; -use sc_cli::CliConfiguration; -use sc_network_common::role::Role; -use sc_service::config::KeystoreConfig; -use sc_service::KeystoreContainer; -use std::path::PathBuf; -use tc_consensus::collators::lookahead::BuyCoreParams; use { + crate::command::solochain::{ + build_solochain_config_dir, build_solochain_net_config_dir, copy_zombienet_keystore, + dummy_config, + }, cumulus_client_cli::CollatorOptions, cumulus_client_collator::service::CollatorService, cumulus_client_consensus_proposer::Proposer, @@ -60,13 +54,18 @@ use { polkadot_cli::ProvideRuntimeApi, polkadot_parachain_primitives::primitives::HeadData, polkadot_service::Handle, + sc_cli::CliConfiguration, sc_client_api::{ AuxStore, Backend as BackendT, BlockchainEvents, HeaderBackend, UsageProvider, }, sc_consensus::BasicQueue, sc_network::NetworkBlock, + sc_network_common::role::Role, sc_network_sync::SyncingService, - sc_service::{Configuration, SpawnTaskHandle, TFullBackend, TaskManager}, + sc_service::{ + config::KeystoreConfig, Configuration, KeystoreContainer, SpawnTaskHandle, TFullBackend, + TaskManager, + }, sc_telemetry::TelemetryHandle, sc_transaction_pool::FullPool, sp_api::StorageProof, @@ -75,10 +74,10 @@ use { sp_core::{traits::SpawnEssentialNamed, H256}, sp_keystore::KeystorePtr, sp_state_machine::{Backend as StateBackend, StorageValue}, - std::{pin::Pin, sync::Arc, time::Duration}, + std::{path::PathBuf, pin::Pin, sync::Arc, time::Duration}, tc_consensus::{ collators::lookahead::{ - self as lookahead_tanssi_aura, Params as LookaheadTanssiAuraParams, + self as lookahead_tanssi_aura, BuyCoreParams, Params as LookaheadTanssiAuraParams, }, OnDemandBlockProductionApi, OrchestratorAuraWorkerAuxData, TanssiAuthorityAssignmentApi, }, @@ -714,64 +713,35 @@ pub async fn start_solochain_node( let chain_type = polkadot_config.chain_spec.chain_type().clone(); let relay_chain = polkadot_config.chain_spec.id().to_string(); - let (telemetry_worker_handle, mut task_manager, keystore_container) = { - let base_path = container_chain_cli - .base - .base - .shared_params - .base_path - .as_ref() - .expect("base_path is always set"); - let config_dir = build_solochain_config_dir(&base_path); - let _net_config_dir = build_solochain_net_config_dir(&config_dir); - let keystore = keystore_config(container_chain_cli.keystore_params(), &config_dir) - .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - - // Instead of putting keystore in - // Collator1000-01/data/chains/simple_container_2000/keystore - // We put it in - // Collator1000-01/data/config/keystore - // And same for "network" folder - // But zombienet will put the keys in the old path, so we need to manually copy it if we - // are running under zombienet - copy_zombienet_keystore(&keystore)?; - - let keystore_container = KeystoreContainer::new(&keystore)?; - - let task_manager = { - //let registry = config.prometheus_config.as_ref().map(|cfg| &cfg.registry); - let registry = None; - TaskManager::new(tokio_handle.clone(), registry)? - }; - - // TODO: impl telemetry - /* - let telemetry = parachain_config - .telemetry_endpoints - .clone() - .filter(|x| !x.is_empty()) - .map(|endpoints| -> Result<_, sc_telemetry::Error> { - let worker = TelemetryWorker::new(16)?; - let telemetry = worker.handle().new_telemetry(endpoints); - Ok((worker, telemetry)) - }) - .transpose()?; - + let base_path = container_chain_cli + .base + .base + .shared_params + .base_path + .as_ref() + .expect("base_path is always set"); + let config_dir = build_solochain_config_dir(&base_path); + let _net_config_dir = build_solochain_net_config_dir(&config_dir); + let keystore = keystore_config(container_chain_cli.keystore_params(), &config_dir) + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); + // Instead of putting keystore in + // Collator1000-01/data/chains/simple_container_2000/keystore + // We put it in + // Collator1000-01/data/config/keystore + // And same for "network" folder + // But zombienet will put the keys in the old path, so we need to manually copy it if we + // are running under zombienet + copy_zombienet_keystore(&keystore)?; - let telemetry = telemetry.map(|(worker, telemetry)| { - task_manager - .spawn_handle() - .spawn("telemetry", None, worker.run()); - telemetry - }); + let keystore_container = KeystoreContainer::new(&keystore)?; - */ - let telemetry_worker_handle = None; + // No metrics so no prometheus registry + let prometheus_registry = None; + let mut task_manager = TaskManager::new(tokio_handle.clone(), prometheus_registry)?; - (telemetry_worker_handle, task_manager, keystore_container) - }; + // Each container chain will spawn its own telemetry + let telemetry_worker_handle = None; // Dummy parachain config only needed because `build_relay_chain_interface` needs to know if we // are collators or not From fe18b299697640e167293537a67741ca2a72dadd Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Fri, 20 Sep 2024 11:38:57 +0200 Subject: [PATCH 08/16] Add solochain-key subcommand --- node/src/cli.rs | 4 ++++ node/src/command.rs | 25 ++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/node/src/cli.rs b/node/src/cli.rs index a01edcfb4..b17666a42 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -62,6 +62,10 @@ pub enum Subcommand { #[command(subcommand)] Key(KeyCmd), + /// Same as key but using a different default keystore path + #[command(subcommand)] + SolochainKey(KeyCmd), + /// Precompile the WASM runtime into native code PrecompileWasm(sc_cli::PrecompileWasmCmd), diff --git a/node/src/command.rs b/node/src/command.rs index e159b2ce2..939757050 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -194,8 +194,27 @@ macro_rules! construct_async_run { /// Parse command line arguments into service configuration. pub fn run() -> Result<()> { - let cli = Cli::from_args(); + let mut cli = Cli::from_args(); + + // If subcommand is `solochain-key`, modify default value of `--keystore-path` to be + // `base_path/config/keystore` instead of `base_path/chains/dancebox/keystore`. + if matches!(&cli.subcommand, Some(Subcommand::SolochainKey(_cmd))) { + if cli.run.base.base.keystore_params.keystore_path.is_none() { + let base_path = cli + .run + .base + .base + .shared_params + .base_path()? + .unwrap_or_else(|| BasePath::from_project("", "", &Cli::executable_name())); + + let keystore_path = base_path.path().join("config/keystore"); + + cli.run.base.base.keystore_params.keystore_path = Some(keystore_path); + } + } + let cli = cli; match &cli.subcommand { Some(Subcommand::BuildSpec(cmd)) => { let runner = cli.create_runner(cmd)?; @@ -330,6 +349,10 @@ pub fn run() -> Result<()> { } } Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?), + Some(Subcommand::SolochainKey(cmd)) => { + // Same as Key but set a different keystore-path by default, set above + Ok(cmd.run(&cli)?) + } Some(Subcommand::PrecompileWasm(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { From b098a0e75637ad449b07fba60dba102d2255d341 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Fri, 20 Sep 2024 11:59:24 +0200 Subject: [PATCH 09/16] Cleanup normalize cmd --- client/service-container-chain/src/cli.rs | 26 ++++++++------ container-chains/nodes/simple/src/command.rs | 18 +--------- node/src/command.rs | 4 +-- node/src/command/solochain.rs | 37 +++++++++++--------- node/src/service.rs | 20 +---------- 5 files changed, 41 insertions(+), 64 deletions(-) diff --git a/client/service-container-chain/src/cli.rs b/client/service-container-chain/src/cli.rs index 79bd41a89..62e41fbda 100644 --- a/client/service-container-chain/src/cli.rs +++ b/client/service-container-chain/src/cli.rs @@ -74,8 +74,6 @@ pub struct ContainerChainRunCmd { impl ContainerChainRunCmd { /// Create a [`NormalizedRunCmd`] which merges the `collator` cli argument into `validator` to /// have only one. - // Copied from polkadot-sdk/cumulus/client/cli/src/lib.rs - // TODO: deduplicate this function and [ContainerChainCli::new] pub fn normalize(&self) -> ContainerChainCli { let mut new_base = self.clone(); @@ -149,16 +147,24 @@ impl ContainerChainCli { let mut base: ContainerChainRunCmd = clap::Parser::parse_from(container_chain_args); // Copy some parachain args into container chain args - // TODO: warn the user if they try to set one of these args using container chain args, - // because that args will be ignored - let base_path = para_config.base_path.path().join("containers"); - base.base.shared_params.base_path = Some(base_path); - // TODO: move wasmtime_precompiled here - Self { - base, - preloaded_chain_spec: None, + // If the container chain args have no --wasmtime-precompiled flag, use the same as the orchestrator + if base.base.import_params.wasmtime_precompiled.is_none() { + base.base + .import_params + .wasmtime_precompiled + .clone_from(¶_config.wasmtime_precompiled); } + + // Set container base path to the same value as orchestrator base_path. + // "containers" is appended in `base.normalize()` + if base.base.shared_params.base_path.is_some() { + log::warn!("Container chain --base-path is being ignored"); + } + let base_path = para_config.base_path.path().to_owned(); + base.base.shared_params.base_path = Some(base_path); + + base.normalize() } pub fn chain_spec_from_genesis_data( diff --git a/container-chains/nodes/simple/src/command.rs b/container-chains/nodes/simple/src/command.rs index fbf171eeb..c6326a9cb 100644 --- a/container-chains/nodes/simple/src/command.rs +++ b/container-chains/nodes/simple/src/command.rs @@ -528,29 +528,13 @@ fn rpc_provider_mode(cli: Cli, profile_id: u64) -> Result<()> { // Spawn assignment watcher { - let mut container_chain_cli = ContainerChainCli::new( + let container_chain_cli = ContainerChainCli::new( &config, [ContainerChainCli::executable_name()] .iter() .chain(cli.container_chain_args().iter()), ); - // If the container chain args have no --wasmtime-precompiled flag, use the same as the orchestrator - if container_chain_cli - .base - .base - .import_params - .wasmtime_precompiled - .is_none() - { - container_chain_cli - .base - .base - .import_params - .wasmtime_precompiled - .clone_from(&config.wasmtime_precompiled); - } - log::info!("Container chain CLI: {container_chain_cli:?}"); let para_id = chain_spec::Extensions::try_get(&*config.chain_spec) diff --git a/node/src/command.rs b/node/src/command.rs index 939757050..5409bf586 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -378,13 +378,13 @@ pub fn run() -> Result<()> { // Zombienet appends a --chain flag after "solo-chain" subcommand, which is ignored, so it's fine, // but warn users that this is not expected here. // We cannot do this before create_runner because logging is not setup there yet. - if cmd.run.base.shared_params.chain.is_some() { + if container_chain_cli.base.base.shared_params.chain.is_some() { log::warn!( "Ignoring --chain argument: solochain mode does only need the relay chain-spec" ); } - let collator_options = cmd.run.collator_options(); + let collator_options = container_chain_cli.base.collator_options(); runner.run_node_until_exit(|config| async move { let containers_base_path = container_chain_cli diff --git a/node/src/command/solochain.rs b/node/src/command/solochain.rs index 0a19459ac..853a4178f 100644 --- a/node/src/command/solochain.rs +++ b/node/src/command/solochain.rs @@ -16,22 +16,27 @@ //! Helper functions used to implement solochain collator -use crate::cli::{Cli, RelayChainCli}; -use futures::FutureExt; -use jsonrpsee::server::BatchRequestConfig; -use log::{info, warn}; -use sc_chain_spec::{ChainType, GenericChainSpec, NoExtension}; -use sc_cli::{CliConfiguration, DefaultConfigurationValues, Signals, SubstrateCli}; -use sc_network::config::{NetworkBackendType, NetworkConfiguration, TransportConfig}; -use sc_network_common::role::Role; -use sc_service::config::KeystoreConfig; -use sc_service::{BasePath, BlocksPruning, Configuration, DatabaseSource, TaskManager}; -use sc_tracing::logging::LoggerBuilder; -use std::future::Future; -use std::num::NonZeroUsize; -use std::path::{Path, PathBuf}; -use std::time::Duration; -use tc_service_container_chain::cli::ContainerChainCli; +use { + crate::cli::{Cli, RelayChainCli}, + futures::FutureExt, + jsonrpsee::server::BatchRequestConfig, + log::{info, warn}, + sc_chain_spec::{ChainType, GenericChainSpec, NoExtension}, + sc_cli::{CliConfiguration, DefaultConfigurationValues, Signals, SubstrateCli}, + sc_network::config::{NetworkBackendType, NetworkConfiguration, TransportConfig}, + sc_network_common::role::Role, + sc_service::{ + config::KeystoreConfig, BasePath, BlocksPruning, Configuration, DatabaseSource, TaskManager, + }, + sc_tracing::logging::LoggerBuilder, + std::{ + future::Future, + num::NonZeroUsize, + path::{Path, PathBuf}, + time::Duration, + }, + tc_service_container_chain::cli::ContainerChainCli, +}; /// Alternative to [Configuration] struct used in solochain context. pub struct SolochainConfig { diff --git a/node/src/service.rs b/node/src/service.rs index 2aebabe9e..7c5b47de4 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -245,30 +245,12 @@ pub fn import_queue( async fn start_node_impl( orchestrator_config: Configuration, polkadot_config: Configuration, - mut container_chain_config: Option<(ContainerChainCli, tokio::runtime::Handle)>, + container_chain_config: Option<(ContainerChainCli, tokio::runtime::Handle)>, collator_options: CollatorOptions, para_id: ParaId, hwbench: Option, ) -> sc_service::error::Result<(TaskManager, Arc)> { let parachain_config = prepare_node_config(orchestrator_config); - if let Some((container_chain_cli, _)) = &mut container_chain_config { - // If the container chain args have no --wasmtime-precompiled flag, use the same as the orchestrator - if container_chain_cli - .base - .base - .import_params - .wasmtime_precompiled - .is_none() - { - container_chain_cli - .base - .base - .import_params - .wasmtime_precompiled - .clone_from(¶chain_config.wasmtime_precompiled); - } - } - let chain_type: sc_chain_spec::ChainType = parachain_config.chain_spec.chain_type(); let relay_chain = crate::chain_spec::Extensions::try_get(&*parachain_config.chain_spec) .map(|e| e.relay_chain.clone()) From e61fa2f66f3abf8901d9d5b4a9b16b48ae362ac5 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Fri, 20 Sep 2024 13:03:10 +0200 Subject: [PATCH 10/16] Remove solochain-key subcommand, it does not work --- node/src/cli.rs | 4 ---- node/src/command.rs | 25 +------------------------ 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/node/src/cli.rs b/node/src/cli.rs index b17666a42..a01edcfb4 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -62,10 +62,6 @@ pub enum Subcommand { #[command(subcommand)] Key(KeyCmd), - /// Same as key but using a different default keystore path - #[command(subcommand)] - SolochainKey(KeyCmd), - /// Precompile the WASM runtime into native code PrecompileWasm(sc_cli::PrecompileWasmCmd), diff --git a/node/src/command.rs b/node/src/command.rs index 5409bf586..54122b773 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -194,27 +194,8 @@ macro_rules! construct_async_run { /// Parse command line arguments into service configuration. pub fn run() -> Result<()> { - let mut cli = Cli::from_args(); - - // If subcommand is `solochain-key`, modify default value of `--keystore-path` to be - // `base_path/config/keystore` instead of `base_path/chains/dancebox/keystore`. - if matches!(&cli.subcommand, Some(Subcommand::SolochainKey(_cmd))) { - if cli.run.base.base.keystore_params.keystore_path.is_none() { - let base_path = cli - .run - .base - .base - .shared_params - .base_path()? - .unwrap_or_else(|| BasePath::from_project("", "", &Cli::executable_name())); - - let keystore_path = base_path.path().join("config/keystore"); - - cli.run.base.base.keystore_params.keystore_path = Some(keystore_path); - } - } + let cli = Cli::from_args(); - let cli = cli; match &cli.subcommand { Some(Subcommand::BuildSpec(cmd)) => { let runner = cli.create_runner(cmd)?; @@ -349,10 +330,6 @@ pub fn run() -> Result<()> { } } Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?), - Some(Subcommand::SolochainKey(cmd)) => { - // Same as Key but set a different keystore-path by default, set above - Ok(cmd.run(&cli)?) - } Some(Subcommand::PrecompileWasm(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { From 9968d1fe66ad6dc5cd3af02b459100cd31032a66 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Fri, 20 Sep 2024 16:44:52 +0200 Subject: [PATCH 11/16] Remove unused network config dir --- node/src/command/solochain.rs | 10 +++++++--- node/src/service.rs | 20 +++----------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/node/src/command/solochain.rs b/node/src/command/solochain.rs index 853a4178f..371fb24e4 100644 --- a/node/src/command/solochain.rs +++ b/node/src/command/solochain.rs @@ -348,9 +348,13 @@ pub(crate) fn build_solochain_config_dir(base_path: &PathBuf) -> PathBuf { base_path.join("config") } -/// Returns the default path for the network configuration inside the configuration dir -pub(crate) fn build_solochain_net_config_dir(config_dir: &PathBuf) -> PathBuf { - config_dir.join("network") +pub fn keystore_config( + keystore_params: Option<&sc_cli::KeystoreParams>, + config_dir: &PathBuf, +) -> sc_cli::Result { + keystore_params + .map(|x| x.keystore_config(config_dir)) + .unwrap_or_else(|| Ok(KeystoreConfig::InMemory)) } /// Get the zombienet keystore path from the solochain collator keystore. diff --git a/node/src/service.rs b/node/src/service.rs index 7c5b47de4..cc4e8658f 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -18,8 +18,7 @@ use { crate::command::solochain::{ - build_solochain_config_dir, build_solochain_net_config_dir, copy_zombienet_keystore, - dummy_config, + build_solochain_config_dir, copy_zombienet_keystore, dummy_config, keystore_config, }, cumulus_client_cli::CollatorOptions, cumulus_client_collator::service::CollatorService, @@ -62,10 +61,7 @@ use { sc_network::NetworkBlock, sc_network_common::role::Role, sc_network_sync::SyncingService, - sc_service::{ - config::KeystoreConfig, Configuration, KeystoreContainer, SpawnTaskHandle, TFullBackend, - TaskManager, - }, + sc_service::{Configuration, KeystoreContainer, SpawnTaskHandle, TFullBackend, TaskManager}, sc_telemetry::TelemetryHandle, sc_transaction_pool::FullPool, sp_api::StorageProof, @@ -74,7 +70,7 @@ use { sp_core::{traits::SpawnEssentialNamed, H256}, sp_keystore::KeystorePtr, sp_state_machine::{Backend as StateBackend, StorageValue}, - std::{path::PathBuf, pin::Pin, sync::Arc, time::Duration}, + std::{pin::Pin, sync::Arc, time::Duration}, tc_consensus::{ collators::lookahead::{ self as lookahead_tanssi_aura, BuyCoreParams, Params as LookaheadTanssiAuraParams, @@ -673,15 +669,6 @@ pub async fn start_parachain_node( .await } -fn keystore_config( - keystore_params: Option<&sc_cli::KeystoreParams>, - config_dir: &PathBuf, -) -> sc_cli::Result { - keystore_params - .map(|x| x.keystore_config(config_dir)) - .unwrap_or_else(|| Ok(KeystoreConfig::InMemory)) -} - /// Start a solochain node. pub async fn start_solochain_node( polkadot_config: Configuration, @@ -703,7 +690,6 @@ pub async fn start_solochain_node( .as_ref() .expect("base_path is always set"); let config_dir = build_solochain_config_dir(&base_path); - let _net_config_dir = build_solochain_net_config_dir(&config_dir); let keystore = keystore_config(container_chain_cli.keystore_params(), &config_dir) .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; From f8e3201160a750b9e7639a8c1da47223bba36eb1 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Fri, 20 Sep 2024 16:56:13 +0200 Subject: [PATCH 12/16] zombie_tanssi_relay change collator names --- test/configs/zombieStarlight.json | 12 ++++++------ test/scripts/build-spec-starlight.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/configs/zombieStarlight.json b/test/configs/zombieStarlight.json index 098513146..cc2e02392 100644 --- a/test/configs/zombieStarlight.json +++ b/test/configs/zombieStarlight.json @@ -60,7 +60,7 @@ "prometheus_port": 33102 }, { - "name": "Collator1000-01", + "name": "Collator-01", "command": "../target/release/tanssi-node solo-chain", "args": [ "--no-hardware-benchmarks", @@ -70,7 +70,7 @@ "prometheus_port": 33102 }, { - "name": "Collator1000-02", + "name": "Collator-02", "command": "../target/release/tanssi-node solo-chain", "args": [ "--no-hardware-benchmarks", @@ -80,7 +80,7 @@ "prometheus_port": 33102 }, { - "name": "Collator1000-03", + "name": "Collator-03", "command": "../target/release/tanssi-node solo-chain", "args": [ "--no-hardware-benchmarks", @@ -90,7 +90,7 @@ "prometheus_port": 33102 }, { - "name": "Collator1000-04", + "name": "Collator-04", "command": "../target/release/tanssi-node solo-chain", "args": [ "--no-hardware-benchmarks", @@ -100,7 +100,7 @@ "prometheus_port": 33102 }, { - "name": "Collator2000-01", + "name": "Collator-05", "command": "../target/release/tanssi-node solo-chain", "args": [ "--no-hardware-benchmarks", @@ -110,7 +110,7 @@ "prometheus_port": 33102 }, { - "name": "Collator2000-02", + "name": "Collator-06", "command": "../target/release/tanssi-node solo-chain", "args": [ "--no-hardware-benchmarks", diff --git a/test/scripts/build-spec-starlight.sh b/test/scripts/build-spec-starlight.sh index 4749b7d3b..be05fc12c 100755 --- a/test/scripts/build-spec-starlight.sh +++ b/test/scripts/build-spec-starlight.sh @@ -16,7 +16,7 @@ mkdir -p specs $BINARY_FOLDER/container-chain-simple-node build-spec --disable-default-bootnode --add-bootnode "/ip4/127.0.0.1/tcp/33049/ws/p2p/12D3KooWHVMhQDHBpj9vQmssgyfspYecgV6e3hH1dQVDUkUbCYC9" --parachain-id 2000 --raw > specs/single-container-template-container-2000.json $BINARY_FOLDER/container-chain-frontier-node build-spec --disable-default-bootnode --add-bootnode "/ip4/127.0.0.1/tcp/33050/ws/p2p/12D3KooWFGaw1rxB6MSuN3ucuBm7hMq5pBFJbEoqTyth4cG483Cc" --parachain-id 2001 --raw > specs/single-container-template-container-2001.json $BINARY_FOLDER/container-chain-simple-node build-spec --disable-default-bootnode --parachain-id 2002 --raw > specs/single-container-template-container-2002.json -$BINARY_FOLDER/tanssi-relay build-spec --chain starlight-local --add-container-chain specs/single-container-template-container-2000.json --add-container-chain specs/single-container-template-container-2001.json --invulnerable "Collator1000-01" --invulnerable "Collator1000-02" --invulnerable "Collator1000-03" --invulnerable "Collator1000-04" --invulnerable "Collator2000-01" --invulnerable "Collator2000-02" > specs/tanssi-relay.json +$BINARY_FOLDER/tanssi-relay build-spec --chain starlight-local --add-container-chain specs/single-container-template-container-2000.json --add-container-chain specs/single-container-template-container-2001.json --invulnerable "Collator-01" --invulnerable "Collator-02" --invulnerable "Collator-03" --invulnerable "Collator-04" --invulnerable "Collator-05" --invulnerable "Collator-06" > specs/tanssi-relay.json # Also need to build the genesis-state to be able to register the container 2002 later $BINARY_FOLDER/container-chain-simple-node export-genesis-state --chain specs/single-container-template-container-2002.json specs/para-2002-genesis-state From c0ebe5761206dce6448d46b50098f66e99679d1a Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Fri, 20 Sep 2024 17:36:52 +0200 Subject: [PATCH 13/16] Change log path in zombienet test --- test/suites/zombie-tanssi-relay/test-tanssi-relay.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/suites/zombie-tanssi-relay/test-tanssi-relay.ts b/test/suites/zombie-tanssi-relay/test-tanssi-relay.ts index 967ce6cea..8657ca269 100644 --- a/test/suites/zombie-tanssi-relay/test-tanssi-relay.ts +++ b/test/suites/zombie-tanssi-relay/test-tanssi-relay.ts @@ -386,12 +386,12 @@ describeSuite({ timeout: 300000, test: async function () { const logs = [ - "/Collator1000-01.log", - "/Collator1000-02.log", - "/Collator1000-03.log", - "/Collator1000-04.log", - "/Collator2000-01.log", - "/Collator2000-02.log", + "/Collator-01.log", + "/Collator-02.log", + "/Collator-03.log", + "/Collator-04.log", + "/Collator-05.log", + "/Collator-06.log", ]; for (const log of logs) { const logFilePath = getTmpZombiePath() + log; From bb61c1e2761ec88e31cef1683d766cc1139afa37 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Mon, 23 Sep 2024 12:06:55 +0200 Subject: [PATCH 14/16] Fix panic if no --base-path arg --- client/service-container-chain/src/cli.rs | 25 +++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/client/service-container-chain/src/cli.rs b/client/service-container-chain/src/cli.rs index 62e41fbda..8aee43a15 100644 --- a/client/service-container-chain/src/cli.rs +++ b/client/service-container-chain/src/cli.rs @@ -20,8 +20,9 @@ use { dc_orchestrator_chain_interface::ContainerChainGenesisData, dp_container_chain_genesis_data::json::properties_to_map, sc_chain_spec::ChainSpec, - sc_cli::CliConfiguration, + sc_cli::{CliConfiguration, SubstrateCli}, sc_network::config::MultiaddrWithPeerId, + sc_service::BasePath, sp_runtime::Storage, std::{collections::BTreeMap, net::SocketAddr}, url::Url, @@ -83,16 +84,10 @@ impl ContainerChainRunCmd { // a new container chain, its database is always inside the `containers` folder. // So if the user passes `--base-path /tmp/node`, we want the ephemeral container data in // `/tmp/node/containers`, and the persistent storage in `/tmp/node/config`. - // TODO: there should be a way to avoid this if we refactor the code that creates the db, - // but maybe that breaks dancebox - let base_path = match self.base.base_path() { - Ok(Some(x)) => x, - _ => { - // This is maybe unreachable. There is always a default base path, and if run in - // `--dev` or `--tmp` mode, a temporary base path is created. - panic!("No base path") - } - }; + let base_path = base_path_or_default( + self.base.base_path().expect("failed to get base_path"), + &ContainerChainCli::executable_name(), + ); let base_path = base_path.path().join("containers"); new_base.base.shared_params.base_path = Some(base_path); @@ -460,3 +455,11 @@ fn validate_relay_chain_url(arg: &str) -> Result { )) } } + +/// Returns the value of `base_path` or the default_path if it is None +pub(crate) fn base_path_or_default( + base_path: Option, + executable_name: &String, +) -> BasePath { + base_path.unwrap_or_else(|| BasePath::from_project("", "", executable_name)) +} From 243c3f1a5de7478d72be8d0a2f3248be1ab838ec Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Tue, 24 Sep 2024 11:38:00 +0200 Subject: [PATCH 15/16] expect instead of unwrap --- client/service-container-chain/src/service.rs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/client/service-container-chain/src/service.rs b/client/service-container-chain/src/service.rs index d3c8dfa9f..d1112c3b8 100644 --- a/client/service-container-chain/src/service.rs +++ b/client/service-container-chain/src/service.rs @@ -330,8 +330,12 @@ fn start_consensus_container( u64::try_from(relay_slot_ms).expect("relay chain slot duration overflows u64"), ) } else { - cumulus_client_consensus_aura::slot_duration(orchestrator_client.as_deref().unwrap()) - .expect("start_consensus_container: slot duration should exist") + cumulus_client_consensus_aura::slot_duration( + orchestrator_client + .as_deref() + .expect("solochain is false, orchestrator_client must be Some"), + ) + .expect("start_consensus_container: slot duration should exist") }; let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( @@ -369,8 +373,10 @@ fn start_consensus_container( BuyCoreParams::Solochain {} } else { BuyCoreParams::Orchestrator { - orchestrator_tx_pool: orchestrator_tx_pool.unwrap(), - orchestrator_client: orchestrator_client.unwrap(), + orchestrator_tx_pool: orchestrator_tx_pool + .expect("solochain is false, orchestrator_tx_pool must be Some"), + orchestrator_client: orchestrator_client + .expect("solochain is false, orchestrator_client must be Some"), } }; @@ -489,7 +495,9 @@ fn start_consensus_container( })?; let authorities = tc_consensus::authorities::( - orchestrator_client_for_cidp.as_ref().unwrap(), + orchestrator_client_for_cidp + .as_ref() + .expect("solochain is false, orchestrator_client must be Some"), &latest_header.hash(), para_id, ); @@ -507,7 +515,9 @@ fn start_consensus_container( ); let slot_freq = tc_consensus::min_slot_freq::( - orchestrator_client_for_cidp.as_ref().unwrap(), + orchestrator_client_for_cidp + .as_ref() + .expect("solochain is false, orchestrator_client must be Some"), &latest_header.hash(), para_id, ); From 83a54351439bfed4f08f1d6caf129ad219f4e798 Mon Sep 17 00:00:00 2001 From: Tomasz Polaczyk Date: Tue, 24 Sep 2024 15:29:51 +0200 Subject: [PATCH 16/16] Improve TODO --- node/src/command/solochain.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/src/command/solochain.rs b/node/src/command/solochain.rs index 371fb24e4..30b658abc 100644 --- a/node/src/command/solochain.rs +++ b/node/src/command/solochain.rs @@ -172,7 +172,8 @@ pub fn create_runner, DVC: DefaultConfigurationValues>( let network_node_name = command.node_name()?; let is_dev = command.is_dev()?; let role = command.role(is_dev)?; - // TODO: where to get relay_chain_id from? + // This relay chain id is only used when the relay chain args have no `--chain` value + // TODO: check if this works with an external relay rpc / light client let relay_chain_id = "starlight_local_testnet".to_string(); let config = SolochainConfig {