diff --git a/Cargo.lock b/Cargo.lock index 05e93e8895d1..ef13d384b3cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8623,8 +8623,11 @@ dependencies = [ "polkadot-collator", "polkadot-parachain", "polkadot-primitives", + "polkadot-service", "sc-client-api", + "sp-api", "sp-core", + "sp-runtime", "test-parachain-adder", ] diff --git a/collator/src/lib.rs b/collator/src/lib.rs index 070740bf7ff8..95db30aacbbf 100644 --- a/collator/src/lib.rs +++ b/collator/src/lib.rs @@ -67,7 +67,7 @@ pub use sc_network::PeerId; pub use service::{RuntimeApiCollection, Client}; pub use sc_cli::SubstrateCli; #[cfg(not(feature = "service-rewr"))] -use polkadot_service::{FullNodeHandles, AbstractClient}; +use polkadot_service::{FullNodeHandles, AbstractClient, ClientHandle}; #[cfg(feature = "service-rewr")] use polkadot_service_new::{ self as polkadot_service, @@ -76,7 +76,8 @@ use polkadot_service_new::{ use sc_service::SpawnTaskHandle; use sp_core::traits::SpawnNamed; use sp_runtime::traits::BlakeTwo256; -use consensus_common::SyncOracle; +pub use consensus_common::SyncOracle; +use sc_client_api::Backend as BackendT; const COLLATION_TIMEOUT: Duration = Duration::from_secs(30); @@ -117,14 +118,20 @@ pub trait BuildParachainContext { type ParachainContext: self::ParachainContext; /// Build the `ParachainContext`. - fn build( + fn build( self, - client: polkadot_service::Client, + client: Arc, spawner: SP, - network: impl Network + SyncOracle + Clone + 'static, + network: PNetwork, ) -> Result where - SP: SpawnNamed + Clone + Send + Sync + 'static; + SP: SpawnNamed + Clone + Send + Sync + 'static, + Backend: BackendT, + Backend::State: sp_api::StateBackend, + Client: polkadot_service::AbstractClient + 'static, + Client::Api: RuntimeApiCollection, + PNetwork: Network + SyncOracle + Clone + 'static, + ; } /// Parachain context needed for collation. @@ -194,11 +201,12 @@ pub async fn collate

( Some(collation) } +/// Build a collator service based on the `ClientHandle`. #[cfg(feature = "service-rewr")] -fn build_collator_service

( +pub fn build_collator_service

( spawner: SpawnTaskHandle, handles: FullNodeHandles, - client: polkadot_service::Client, + client: impl ClientHandle, para_id: ParaId, key: Arc, build_parachain_context: P, @@ -217,7 +225,6 @@ struct BuildCollationWork

{ key: Arc, build_parachain_context: P, spawner: SpawnTaskHandle, - client: polkadot_service::Client, } impl

polkadot_service::ExecuteWithClient for BuildCollationWork

@@ -233,7 +240,7 @@ impl

polkadot_service::ExecuteWithClient for BuildCollationWork

Backend: sc_client_api::Backend, Backend::State: sp_api::StateBackend, Api: RuntimeApiCollection, - Client: AbstractClient + 'static + Client: AbstractClient + 'static, { let polkadot_network = self.handles .polkadot_network @@ -246,7 +253,7 @@ impl

polkadot_service::ExecuteWithClient for BuildCollationWork

.ok_or_else(|| "Collator cannot run when validation networking has not been started")?; let parachain_context = match self.build_parachain_context.build( - self.client, + client.clone(), self.spawner.clone(), polkadot_network.clone(), ) { @@ -346,11 +353,12 @@ impl

polkadot_service::ExecuteWithClient for BuildCollationWork

} } +/// Build a collator service based on the `ClientHandle`. #[cfg(not(feature = "service-rewr"))] -fn build_collator_service

( +pub fn build_collator_service

( spawner: SpawnTaskHandle, handles: FullNodeHandles, - client: polkadot_service::Client, + client: impl ClientHandle, para_id: ParaId, key: Arc, build_parachain_context: P, @@ -366,7 +374,6 @@ fn build_collator_service

( key, build_parachain_context, spawner, - client: client.clone(), }) } @@ -454,12 +461,20 @@ mod tests { impl BuildParachainContext for BuildDummyParachainContext { type ParachainContext = DummyParachainContext; - fn build( + fn build( self, - _: polkadot_service::Client, + _: Arc, _: SP, - _: impl Network + Clone + 'static, - ) -> Result { + _: PNetwork, + ) -> Result + where + SP: SpawnNamed + Clone + Send + Sync + 'static, + Backend: BackendT, + Backend::State: sp_api::StateBackend, + Client: polkadot_service::AbstractClient + 'static, + Client::Api: RuntimeApiCollection, + PNetwork: Network + SyncOracle + Clone + 'static, + { Ok(DummyParachainContext) } } diff --git a/node/core/candidate-validation/src/lib.rs b/node/core/candidate-validation/src/lib.rs index 8dcc0a574bba..0a59ac3daf85 100644 --- a/node/core/candidate-validation/src/lib.rs +++ b/node/core/candidate-validation/src/lib.rs @@ -33,8 +33,10 @@ use polkadot_primitives::v1::{ ValidationCode, OmittedValidationData, PoV, CandidateDescriptor, LocalValidationData, GlobalValidationData, OccupiedCoreAssumption, Hash, validation_data_hash, }; -use polkadot_parachain::wasm_executor::{self, ValidationPool, ExecutionMode, ValidationError, - InvalidCandidate as WasmInvalidCandidate}; +use polkadot_parachain::wasm_executor::{ + self, ValidationPool, ExecutionMode, ValidationError, + InvalidCandidate as WasmInvalidCandidate, ValidationExecutionMode, +}; use polkadot_parachain::primitives::{ValidationResult as WasmValidationResult, ValidationParams}; use parity_scale_codec::Encode; @@ -73,7 +75,7 @@ async fn run( ) -> SubsystemResult<()> { - let pool = ValidationPool::new(); + let pool = ValidationPool::new(ValidationExecutionMode::ExternalProcessSelfHost); loop { match ctx.recv().await? { diff --git a/node/test-service/src/lib.rs b/node/test-service/src/lib.rs index bc210c2384f4..98e9760df1db 100644 --- a/node/test-service/src/lib.rs +++ b/node/test-service/src/lib.rs @@ -26,7 +26,7 @@ use polkadot_primitives::v0::{ Block, Hash, CollatorId, Id as ParaId, }; use polkadot_runtime_common::{parachains, registrar, BlockHashCount}; -use polkadot_service::{new_full, FullNodeHandles, AbstractClient}; +use polkadot_service::{new_full, FullNodeHandles, AbstractClient, ClientHandle, ExecuteWithClient}; use polkadot_test_runtime::{RestrictFunctionality, Runtime, SignedExtra, SignedPayload, VERSION}; use sc_chain_spec::ChainSpec; use sc_client_api::{execution_extensions::ExecutionStrategies, BlockchainEvents}; @@ -67,7 +67,7 @@ pub fn polkadot_test_new_full( ) -> Result< ( TaskManager, - Arc>>, + Arc>, FullNodeHandles, Arc>, Arc, @@ -88,6 +88,15 @@ pub fn polkadot_test_new_full( Ok((task_manager, client, handles, network, rpc_handlers)) } +/// A wrapper for the test client that implements `ClientHandle`. +pub struct TestClient(pub Arc>); + +impl ClientHandle for TestClient { + fn execute_with(&self, t: T) -> T::Output { + T::execute_with_client::<_, _, polkadot_service::FullBackend>(t, self.0.clone()) + } +} + /// Create a Polkadot `Configuration`. By default an in-memory socket will be used, therefore you need to provide boot /// nodes if you want the future node to be connected to other nodes. The `storage_update_func` can be used to make /// adjustements to the runtime before the node starts. diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 5ee0a05c699e..abfda8010962 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -19,7 +19,7 @@ polkadot-core-primitives = { path = "../core-primitives", default-features = fal derive_more = { version = "0.99.2", optional = true } serde = { version = "1.0.102", default-features = false, features = [ "derive" ], optional = true } sp-externalities = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch", optional = true } -sc-executor = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch", optional = true, features = ["wasmtime"] } +sc-executor = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch", optional = true } sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch", optional = true } parking_lot = { version = "0.10.0", optional = true } log = { version = "0.4.8", optional = true } diff --git a/parachain/src/wasm_executor/mod.rs b/parachain/src/wasm_executor/mod.rs index a82c123e50f0..d5890e2a878e 100644 --- a/parachain/src/wasm_executor/mod.rs +++ b/parachain/src/wasm_executor/mod.rs @@ -28,7 +28,7 @@ use sp_externalities::Extensions; use sp_wasm_interface::HostFunctions as _; #[cfg(not(any(target_os = "android", target_os = "unknown")))] -pub use validation_host::{run_worker, ValidationPool, EXECUTION_TIMEOUT_SEC}; +pub use validation_host::{run_worker, ValidationPool, EXECUTION_TIMEOUT_SEC, ValidationExecutionMode}; mod validation_host; @@ -66,8 +66,6 @@ pub enum ExecutionMode<'a> { Local, /// Remote execution in a spawned process. Remote(&'a ValidationPool), - /// Remote execution in a spawned test runner. - RemoteTest(&'a ValidationPool), } #[derive(Debug, derive_more::Display, derive_more::From)] @@ -143,11 +141,7 @@ pub fn validate_candidate( }, #[cfg(not(any(target_os = "android", target_os = "unknown")))] ExecutionMode::Remote(pool) => { - pool.validate_candidate(validation_code, params, false) - }, - #[cfg(not(any(target_os = "android", target_os = "unknown")))] - ExecutionMode::RemoteTest(pool) => { - pool.validate_candidate(validation_code, params, true) + pool.validate_candidate(validation_code, params) }, #[cfg(any(target_os = "android", target_os = "unknown"))] ExecutionMode::Remote(_pool) => @@ -156,13 +150,6 @@ pub fn validate_candidate( "Remote validator not available".to_string() ) as Box<_> ))), - #[cfg(any(target_os = "android", target_os = "unknown"))] - ExecutionMode::RemoteTest(_pool) => - Err(ValidationError::Internal(InternalError::System( - Box::::from( - "Remote validator not available".to_string() - ) as Box<_> - ))), } } diff --git a/parachain/src/wasm_executor/validation_host.rs b/parachain/src/wasm_executor/validation_host.rs index 238ed65c7a76..249ad964670b 100644 --- a/parachain/src/wasm_executor/validation_host.rs +++ b/parachain/src/wasm_executor/validation_host.rs @@ -16,7 +16,7 @@ #![cfg(not(any(target_os = "android", target_os = "unknown")))] -use std::{process, env, sync::Arc, sync::atomic}; +use std::{process, env, sync::Arc, sync::atomic, path::PathBuf}; use codec::{Decode, Encode}; use crate::primitives::{ValidationParams, ValidationResult}; use super::{ @@ -29,7 +29,6 @@ use log::{debug, trace}; use futures::executor::ThreadPool; use sp_core::traits::SpawnNamed; -const WORKER_ARGS_TEST: &[&'static str] = &["--nocapture", "validation_worker"]; /// CLI Argument to start in validation worker mode. const WORKER_ARG: &'static str = "validation-worker"; const WORKER_ARGS: &[&'static str] = &[WORKER_ARG]; @@ -66,19 +65,40 @@ impl SpawnNamed for TaskExecutor { } } +/// The execution mode for the `ValidationPool`. +#[derive(Debug, Clone)] +pub enum ValidationExecutionMode { + /// The validation worker is ran in a thread inside the same process. + InProcess, + /// The validation worker is ran using the process' executable and the subcommand `validation-worker` is passed + /// following by the address of the shared memory. + ExternalProcessSelfHost, + /// The validation worker is ran using the command provided and the argument provided. The address of the shared + /// memory is added at the end of the arguments. + ExternalProcessCustomHost { + /// Path to the validation worker. The file must exists and be executable. + binary: PathBuf, + /// List of arguments passed to the validation worker. The address of the shared memory will be automatically + /// added after the arguments. + args: Vec, + }, +} + /// A pool of hosts. #[derive(Clone)] pub struct ValidationPool { hosts: Arc>>, + execution_mode: ValidationExecutionMode, } const DEFAULT_NUM_HOSTS: usize = 8; impl ValidationPool { /// Creates a validation pool with the default configuration. - pub fn new() -> ValidationPool { + pub fn new(execution_mode: ValidationExecutionMode) -> ValidationPool { ValidationPool { hosts: Arc::new((0..DEFAULT_NUM_HOSTS).map(|_| Default::default()).collect()), + execution_mode, } } @@ -90,16 +110,15 @@ impl ValidationPool { &self, validation_code: &[u8], params: ValidationParams, - test_mode: bool, ) -> Result { for host in self.hosts.iter() { if let Some(mut host) = host.try_lock() { - return host.validate_candidate(validation_code, params, test_mode); + return host.validate_candidate(validation_code, params, self.execution_mode.clone()); } } // all workers are busy, just wait for the first one - self.hosts[0].lock().validate_candidate(validation_code, params, test_mode) + self.hosts[0].lock().validate_candidate(validation_code, params, self.execution_mode.clone()) } } @@ -208,6 +227,7 @@ unsafe impl Send for ValidationHost {} #[derive(Default)] struct ValidationHost { worker: Option, + worker_thread: Option>>, memory: Option, id: u32, } @@ -233,7 +253,7 @@ impl ValidationHost { Ok(mem_config.create()?) } - fn start_worker(&mut self, test_mode: bool) -> Result<(), InternalError> { + fn start_worker(&mut self, execution_mode: ValidationExecutionMode) -> Result<(), InternalError> { if let Some(ref mut worker) = self.worker { // Check if still alive if let Ok(None) = worker.try_wait() { @@ -241,17 +261,38 @@ impl ValidationHost { return Ok(()); } } + if self.worker_thread.is_some() { + return Ok(()); + } + let memory = Self::create_memory()?; - let self_path = env::current_exe()?; - debug!("Starting worker at {:?}", self_path); - let mut args = if test_mode { WORKER_ARGS_TEST.to_vec() } else { WORKER_ARGS.to_vec() }; - args.push(memory.get_os_path()); - let worker = process::Command::new(self_path) - .args(args) - .stdin(process::Stdio::piped()) - .spawn()?; - self.id = worker.id(); - self.worker = Some(worker); + + let mut run_worker_process = |cmd: PathBuf, args: Vec| -> Result<(), std::io::Error> { + debug!("Starting worker at {:?} with arguments: {:?} and {:?}", cmd, args, memory.get_os_path()); + let worker = process::Command::new(cmd) + .args(args) + .arg(memory.get_os_path()) + .stdin(process::Stdio::piped()) + .spawn()?; + self.id = worker.id(); + self.worker = Some(worker); + Ok(()) + }; + + match execution_mode { + ValidationExecutionMode::InProcess => { + let mem_id = memory.get_os_path().to_string(); + self.worker_thread = Some(std::thread::spawn(move || run_worker(mem_id.as_str()))); + }, + ValidationExecutionMode::ExternalProcessSelfHost => run_worker_process( + env::current_exe()?, + WORKER_ARGS.iter().map(|x| x.to_string()).collect(), + )?, + ValidationExecutionMode::ExternalProcessCustomHost { binary, args } => run_worker_process( + binary, + args, + )?, + }; memory.wait( Event::WorkerReady as usize, @@ -268,13 +309,13 @@ impl ValidationHost { &mut self, validation_code: &[u8], params: ValidationParams, - test_mode: bool, + execution_mode: ValidationExecutionMode, ) -> Result { if validation_code.len() > MAX_CODE_MEM { return Err(ValidationError::InvalidCandidate(InvalidCandidate::CodeTooLarge(validation_code.len()))); } // First, check if need to spawn the child process - self.start_worker(test_mode)?; + self.start_worker(execution_mode)?; let memory = self.memory.as_mut() .expect("memory is always `Some` after `start_worker` completes successfully"); { diff --git a/parachain/test-parachains/adder/collator/Cargo.toml b/parachain/test-parachains/adder/collator/Cargo.toml index d91f9b60037e..6c7f3ca86e8c 100644 --- a/parachain/test-parachains/adder/collator/Cargo.toml +++ b/parachain/test-parachains/adder/collator/Cargo.toml @@ -9,8 +9,11 @@ adder = { package = "test-parachain-adder", path = ".." } parachain = { package = "polkadot-parachain", path = "../../.." } collator = { package = "polkadot-collator", path = "../../../../collator" } primitives = { package = "polkadot-primitives", path = "../../../../primitives" } +service = { package = "polkadot-service", path = "../../../../service" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" } client-api = { package = "sc-client-api", git = "https://github.com/paritytech/substrate", branch = "rococo-branch" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "rococo-branch" } parking_lot = "0.10.0" codec = { package = "parity-scale-codec", version = "1.3.4" } futures = "0.3.4" diff --git a/parachain/test-parachains/adder/collator/src/main.rs b/parachain/test-parachains/adder/collator/src/main.rs index 2bddff6f8694..351207a6d185 100644 --- a/parachain/test-parachains/adder/collator/src/main.rs +++ b/parachain/test-parachains/adder/collator/src/main.rs @@ -20,15 +20,17 @@ use std::collections::HashMap; use std::sync::Arc; use adder::{HeadData as AdderHead, BlockData as AdderBody}; -use sp_core::Pair; +use sp_core::{traits::SpawnNamed, Pair}; use codec::{Encode, Decode}; use primitives::v0::{ - Hash, DownwardMessage, + Block, Hash, DownwardMessage, HeadData, BlockData, Id as ParaId, LocalValidationData, GlobalValidationData, }; -use collator::{ParachainContext, Network, BuildParachainContext, Cli, SubstrateCli}; +use collator::{ParachainContext, Network, BuildParachainContext, Cli, SubstrateCli, SyncOracle}; use parking_lot::Mutex; use futures::future::{Ready, ready, FutureExt}; +use sp_runtime::traits::BlakeTwo256; +use client_api::Backend as BackendT; const GENESIS: AdderHead = AdderHead { number: 0, @@ -103,12 +105,20 @@ impl ParachainContext for AdderContext { impl BuildParachainContext for AdderContext { type ParachainContext = Self; - fn build( + fn build( self, - _: collator::Client, + _: Arc, _: SP, - network: impl Network + Clone + 'static, - ) -> Result { + network: PNetwork, + ) -> Result + where + SP: SpawnNamed + Clone + Send + Sync + 'static, + Backend: BackendT, + Backend::State: sp_api::StateBackend, + Client: service::AbstractClient + 'static, + Client::Api: service::RuntimeApiCollection, + PNetwork: Network + SyncOracle + Clone + 'static, + { Ok(Self { _network: Some(Arc::new(network)), ..self }) } } diff --git a/parachain/test-parachains/tests/adder/mod.rs b/parachain/test-parachains/tests/adder/mod.rs index ecbac12ac5ab..975cb8d5ae47 100644 --- a/parachain/test-parachains/tests/adder/mod.rs +++ b/parachain/test-parachains/tests/adder/mod.rs @@ -16,11 +16,16 @@ //! Basic parachain that adds a number as part of its state. -use parachain::primitives::{ - RelayChainBlockNumber, - BlockData as GenericBlockData, - HeadData as GenericHeadData, - ValidationParams, +const WORKER_ARGS_TEST: &[&'static str] = &["--nocapture", "validation_worker"]; + +use parachain::{ + primitives::{ + RelayChainBlockNumber, + BlockData as GenericBlockData, + HeadData as GenericHeadData, + ValidationParams, + }, + wasm_executor::{ValidationPool, ValidationExecutionMode} }; use codec::{Decode, Encode}; @@ -52,8 +57,28 @@ fn hash_head(head: &HeadData) -> [u8; 32] { tiny_keccak::keccak256(head.encode().as_slice()) } +fn validation_pool() -> ValidationPool { + let execution_mode = ValidationExecutionMode::ExternalProcessCustomHost { + binary: std::env::current_exe().unwrap(), + args: WORKER_ARGS_TEST.iter().map(|x| x.to_string()).collect(), + }; + + ValidationPool::new(execution_mode) +} + #[test] -pub fn execute_good_on_parent() { +fn execute_good_on_parent_with_inprocess_validation() { + let pool = ValidationPool::new(ValidationExecutionMode::InProcess); + execute_good_on_parent(pool); +} + +#[test] +pub fn execute_good_on_parent_with_external_process_validation() { + let pool = validation_pool(); + execute_good_on_parent(pool); +} + +fn execute_good_on_parent(pool: ValidationPool) { let parent_head = HeadData { number: 0, parent_hash: [0; 32], @@ -65,7 +90,6 @@ pub fn execute_good_on_parent() { add: 512, }; - let pool = parachain::wasm_executor::ValidationPool::new(); let ret = parachain::wasm_executor::validate_candidate( adder::wasm_binary_unwrap(), @@ -77,7 +101,7 @@ pub fn execute_good_on_parent() { relay_chain_height: 1, code_upgrade_allowed: None, }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ).unwrap(); @@ -93,7 +117,7 @@ fn execute_good_chain_on_parent() { let mut number = 0; let mut parent_hash = [0; 32]; let mut last_state = 0; - let pool = parachain::wasm_executor::ValidationPool::new(); + let pool = validation_pool(); for add in 0..10 { let parent_head = HeadData { @@ -117,7 +141,7 @@ fn execute_good_chain_on_parent() { relay_chain_height: number as RelayChainBlockNumber + 1, code_upgrade_allowed: None, }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ).unwrap(); @@ -135,7 +159,7 @@ fn execute_good_chain_on_parent() { #[test] fn execute_bad_on_parent() { - let pool = parachain::wasm_executor::ValidationPool::new(); + let pool = validation_pool(); let parent_head = HeadData { number: 0, @@ -158,7 +182,7 @@ fn execute_bad_on_parent() { relay_chain_height: 1, code_upgrade_allowed: None, }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ).unwrap_err(); } diff --git a/parachain/test-parachains/tests/code_upgrader/mod.rs b/parachain/test-parachains/tests/code_upgrader/mod.rs index b99a6e9dbf45..e2ec3d9fede0 100644 --- a/parachain/test-parachains/tests/code_upgrader/mod.rs +++ b/parachain/test-parachains/tests/code_upgrader/mod.rs @@ -16,6 +16,7 @@ //! Basic parachain that adds a number as part of its state. +use parachain::wasm_executor::{ValidationPool, ValidationExecutionMode}; use parachain::primitives::{ BlockData as GenericBlockData, HeadData as GenericHeadData, @@ -24,9 +25,20 @@ use parachain::primitives::{ use codec::{Decode, Encode}; use code_upgrader::{hash_state, HeadData, BlockData, State}; +const WORKER_ARGS_TEST: &[&'static str] = &["--nocapture", "validation_worker"]; + +fn validation_pool() -> ValidationPool { + let execution_mode = ValidationExecutionMode::ExternalProcessCustomHost { + binary: std::env::current_exe().unwrap(), + args: WORKER_ARGS_TEST.iter().map(|x| x.to_string()).collect(), + }; + + ValidationPool::new(execution_mode) +} + #[test] pub fn execute_good_no_upgrade() { - let pool = parachain::wasm_executor::ValidationPool::new(); + let pool = validation_pool(); let parent_head = HeadData { number: 0, @@ -49,7 +61,7 @@ pub fn execute_good_no_upgrade() { relay_chain_height: 1, code_upgrade_allowed: None, }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ).unwrap(); @@ -63,7 +75,7 @@ pub fn execute_good_no_upgrade() { #[test] pub fn execute_good_with_upgrade() { - let pool = parachain::wasm_executor::ValidationPool::new(); + let pool = validation_pool(); let parent_head = HeadData { number: 0, @@ -86,7 +98,7 @@ pub fn execute_good_with_upgrade() { relay_chain_height: 1, code_upgrade_allowed: Some(20), }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ).unwrap(); @@ -107,7 +119,7 @@ pub fn execute_good_with_upgrade() { #[test] #[should_panic] pub fn code_upgrade_not_allowed() { - let pool = parachain::wasm_executor::ValidationPool::new(); + let pool = validation_pool(); let parent_head = HeadData { number: 0, @@ -130,14 +142,14 @@ pub fn code_upgrade_not_allowed() { relay_chain_height: 1, code_upgrade_allowed: None, }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ).unwrap(); } #[test] pub fn applies_code_upgrade_after_delay() { - let pool = parachain::wasm_executor::ValidationPool::new(); + let pool = validation_pool(); let (new_head, state) = { let parent_head = HeadData { @@ -161,7 +173,7 @@ pub fn applies_code_upgrade_after_delay() { relay_chain_height: 1, code_upgrade_allowed: Some(2), }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ).unwrap(); @@ -197,7 +209,7 @@ pub fn applies_code_upgrade_after_delay() { relay_chain_height: 2, code_upgrade_allowed: None, }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ).unwrap(); diff --git a/parachain/test-parachains/tests/wasm_executor/mod.rs b/parachain/test-parachains/tests/wasm_executor/mod.rs index 769ad737ce9f..ce17412bff13 100644 --- a/parachain/test-parachains/tests/wasm_executor/mod.rs +++ b/parachain/test-parachains/tests/wasm_executor/mod.rs @@ -16,15 +16,26 @@ //! Basic parachain that adds a number as part of its state. +const WORKER_ARGS_TEST: &[&'static str] = &["--nocapture", "validation_worker"]; + use crate::adder; use parachain::{ primitives::{BlockData, ValidationParams}, - wasm_executor::{ValidationError, InvalidCandidate, EXECUTION_TIMEOUT_SEC}, + wasm_executor::{ValidationError, InvalidCandidate, EXECUTION_TIMEOUT_SEC, ValidationExecutionMode, ValidationPool}, }; +fn validation_pool() -> ValidationPool { + let execution_mode = ValidationExecutionMode::ExternalProcessCustomHost { + binary: std::env::current_exe().unwrap(), + args: WORKER_ARGS_TEST.iter().map(|x| x.to_string()).collect(), + }; + + ValidationPool::new(execution_mode) +} + #[test] fn terminates_on_timeout() { - let pool = parachain::wasm_executor::ValidationPool::new(); + let pool = validation_pool(); let result = parachain::wasm_executor::validate_candidate( halt::wasm_binary_unwrap(), @@ -36,7 +47,7 @@ fn terminates_on_timeout() { relay_chain_height: 1, code_upgrade_allowed: None, }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ); match result { @@ -45,12 +56,12 @@ fn terminates_on_timeout() { } // check that another parachain can validate normaly - adder::execute_good_on_parent(); + adder::execute_good_on_parent_with_external_process_validation(); } #[test] fn parallel_execution() { - let pool = parachain::wasm_executor::ValidationPool::new(); + let pool = validation_pool(); let start = std::time::Instant::now(); @@ -66,7 +77,7 @@ fn parallel_execution() { relay_chain_height: 1, code_upgrade_allowed: None, }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool2), + parachain::wasm_executor::ExecutionMode::Remote(&pool2), sp_core::testing::TaskExecutor::new(), ).ok()); let _ = parachain::wasm_executor::validate_candidate( @@ -79,7 +90,7 @@ fn parallel_execution() { relay_chain_height: 1, code_upgrade_allowed: None, }, - parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), + parachain::wasm_executor::ExecutionMode::Remote(&pool), sp_core::testing::TaskExecutor::new(), ); thread.join().unwrap(); diff --git a/service/src/client.rs b/service/src/client.rs index 786d3b8fbabd..b7fc96fbc485 100644 --- a/service/src/client.rs +++ b/service/src/client.rs @@ -118,6 +118,19 @@ pub trait ExecuteWithClient { Client: AbstractClient + 'static; } +/// A handle to a Polkadot client instance. +/// +/// The Polkadot service supports multiple different runtimes (Westend, Polkadot itself, etc). As each runtime has a +/// specialized client, we need to hide them behind a trait. This is this trait. +/// +/// When wanting to work with the inner client, you need to use `execute_with`. +/// +/// See [`ExecuteWithClient`](trait.ExecuteWithClient.html) for more information. +pub trait ClientHandle { + /// Execute the given something with the client. + fn execute_with(&self, t: T) -> T::Output; +} + /// A client instance of Polkadot. /// /// See [`ExecuteWithClient`] for more information. @@ -129,9 +142,8 @@ pub enum Client { Rococo(Arc>), } -impl Client { - /// Execute the given something with the client. - pub fn execute_with(&self, t: T) -> T::Output { +impl ClientHandle for Client { + fn execute_with(&self, t: T) -> T::Output { match self { Self::Polkadot(client) => { T::execute_with_client::<_, _, crate::FullBackend>(t, client.clone()) diff --git a/service/src/lib.rs b/service/src/lib.rs index 5879b10967e1..40c9a54a4a87 100644 --- a/service/src/lib.rs +++ b/service/src/lib.rs @@ -31,6 +31,8 @@ use sc_executor::native_executor_instance; use log::info; use sp_trie::PrefixedMemoryDB; use prometheus_endpoint::Registry; +#[cfg(feature = "full-node")] +use consensus::pipeline::ValidationExecutionMode; pub use service::{ Role, PruningMode, TransactionPoolOptions, Error, RuntimeGenesis, RpcHandlers, TFullClient, TLightClient, TFullBackend, TLightBackend, TFullCallExecutor, TLightCallExecutor, @@ -109,16 +111,25 @@ impl IdentifyVariant for Box { } } -type FullBackend = service::TFullBackend; -type FullSelectChain = sc_consensus::LongestChain; -type FullClient = service::TFullClient; -type FullGrandpaBlockImport = grandpa::GrandpaBlockImport< +/// Polkadot's full backend. +pub type FullBackend = service::TFullBackend; + +/// Polkadot's select chain. +pub type FullSelectChain = sc_consensus::LongestChain; + +/// Polkadot's full client. +pub type FullClient = service::TFullClient; + +/// Polkadot's full Grandpa block import. +pub type FullGrandpaBlockImport = grandpa::GrandpaBlockImport< FullBackend, Block, FullClient, FullSelectChain >; -type LightBackend = service::TLightBackendWithHash; +/// Polkadot's light backend. +pub type LightBackend = service::TLightBackendWithHash; -type LightClient = +/// Polkadot's light client. +pub type LightClient = service::TLightClientWithBackend; #[cfg(feature = "full-node")] @@ -402,6 +413,11 @@ pub fn new_full( select_chain: select_chain.clone(), keystore: keystore.clone(), max_block_data_size, + validation_execution_mode: if test { + ValidationExecutionMode::InProcess + } else { + ValidationExecutionMode::ExternalProcessSelfHost + }, }.build(); task_manager.spawn_essential_handle().spawn("validation-service", Box::pin(validation_service)); diff --git a/validation/src/pipeline.rs b/validation/src/pipeline.rs index a4722ef144a1..51a31ec3070a 100644 --- a/validation/src/pipeline.rs +++ b/validation/src/pipeline.rs @@ -34,7 +34,7 @@ use sp_api::ProvideRuntimeApi; use crate::Error; use primitives::traits::SpawnNamed; -pub use parachain::wasm_executor::ValidationPool; +pub use parachain::wasm_executor::{ValidationPool, ValidationExecutionMode}; /// Does basic checks of a collation. Provide the encoded PoV-block. pub fn basic_checks( diff --git a/validation/src/validation_service/mod.rs b/validation/src/validation_service/mod.rs index 538e987c4392..e06d6a222a6a 100644 --- a/validation/src/validation_service/mod.rs +++ b/validation/src/validation_service/mod.rs @@ -48,7 +48,7 @@ use log::{warn, info, debug, trace}; use super::{Network, Collators, SharedTable, TableRouter}; use crate::Error; -use crate::pipeline::ValidationPool; +use crate::pipeline::{ValidationPool, ValidationExecutionMode}; // Remote processes may request for a validation instance to be cloned or instantiated. // They send a oneshot channel. @@ -132,6 +132,8 @@ pub struct ServiceBuilder { pub keystore: KeyStorePtr, /// The maximum block-data size in bytes. pub max_block_data_size: Option, + /// Validation execution mode. + pub validation_execution_mode: ValidationExecutionMode, } impl ServiceBuilder where @@ -163,7 +165,7 @@ impl ServiceBuilder where NotifyImport(sc_client_api::BlockImportNotification), } - let validation_pool = Some(ValidationPool::new()); + let validation_pool = Some(ValidationPool::new(self.validation_execution_mode)); let mut parachain_validation = ParachainValidationInstances { client: self.client.clone(), network: self.network,