diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index 4d2a3817be057535eabe0fe8a25b7dedd7bb4971..11f82d3d8357fc9abaee18d8fcdb936f292395fc 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -305,7 +305,7 @@ impl CandidateBackingJob { } } } - ValidationResult::Invalid => { + ValidationResult::Invalid(_reason) => { // no need to issue a statement about this if we aren't seconding it. // // there's an infinite amount of garbage out there. no need to acknowledge @@ -497,7 +497,7 @@ impl CandidateBackingJob { Err(()) => Statement::Invalid(candidate_hash), } } - ValidationResult::Invalid => { + ValidationResult::Invalid(_reason) => { Statement::Invalid(candidate_hash) } }; @@ -826,6 +826,7 @@ mod tests { messages::RuntimeApiRequest, ActiveLeavesUpdate, FromOverseer, OverseerSignal, }; + use polkadot_node_primitives::InvalidCandidate; use sp_keyring::Sr25519Keyring; use std::collections::HashMap; @@ -1461,7 +1462,7 @@ mod tests { tx, ) ) if pov == pov && &c == candidate_a.descriptor() => { - tx.send(Ok(ValidationResult::Invalid)).unwrap(); + tx.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap(); } ); @@ -1597,7 +1598,7 @@ mod tests { tx, ) ) if pov == pov && &c == candidate.descriptor() => { - tx.send(Ok(ValidationResult::Invalid)).unwrap(); + tx.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap(); } ); @@ -1729,7 +1730,7 @@ mod tests { tx, ) ) if pov == pov && &c == candidate.descriptor() => { - tx.send(Err(ValidationFailed)).unwrap(); + tx.send(Err(ValidationFailed("Internal test error".into()))).unwrap(); } ); diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index e81f41180dd69e91105b0390bfcb377b60edb3bc..8dcc0a574bbafbc0a65e5764ae580eea293073e5 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -28,12 +28,13 @@ use polkadot_subsystem::messages::{ AllMessages, CandidateValidationMessage, RuntimeApiMessage, ValidationFailed, RuntimeApiRequest, }; use polkadot_subsystem::errors::RuntimeApiError; -use polkadot_node_primitives::{ValidationResult, ValidationOutputs}; +use polkadot_node_primitives::{ValidationResult, ValidationOutputs, InvalidCandidate}; use polkadot_primitives::v1::{ ValidationCode, OmittedValidationData, PoV, CandidateDescriptor, LocalValidationData, GlobalValidationData, OccupiedCoreAssumption, Hash, validation_data_hash, }; -use polkadot_parachain::wasm_executor::{self, ValidationPool, ExecutionMode}; +use polkadot_parachain::wasm_executor::{self, ValidationPool, ExecutionMode, ValidationError, + InvalidCandidate as WasmInvalidCandidate}; use polkadot_parachain::primitives::{ValidationResult as WasmValidationResult, ValidationParams}; use parity_scale_codec::Encode; @@ -241,7 +242,7 @@ async fn spawn_validate_from_chain_state( e, ); - return Ok(Err(ValidationFailed)); + return Ok(Err(ValidationFailed("Error making API request".into()))); } } }; @@ -264,7 +265,7 @@ async fn spawn_validate_from_chain_state( ).await; } AssumptionCheckOutcome::DoesNotMatch => {}, - AssumptionCheckOutcome::BadRequest => return Ok(Err(ValidationFailed)), + AssumptionCheckOutcome::BadRequest => return Ok(Err(ValidationFailed("Bad request".into()))), } match check_assumption_validation_data( @@ -285,13 +286,13 @@ async fn spawn_validate_from_chain_state( ).await; } AssumptionCheckOutcome::DoesNotMatch => {}, - AssumptionCheckOutcome::BadRequest => return Ok(Err(ValidationFailed)), + AssumptionCheckOutcome::BadRequest => return Ok(Err(ValidationFailed("Bad request".into()))), } // If neither the assumption of the occupied core having the para included or the assumption // of the occupied core timing out are valid, then the validation_data_hash in the descriptor // is not based on the relay parent and is thus invalid. - Ok(Ok(ValidationResult::Invalid)) + Ok(Ok(ValidationResult::Invalid(InvalidCandidate::BadParent))) } async fn spawn_validate_exhaustive( @@ -321,52 +322,52 @@ async fn spawn_validate_exhaustive( rx.await.map_err(Into::into) } -/// Does basic checks of a candidate. Provide the encoded PoV-block. Returns `true` if basic checks -/// are passed, false otherwise. -fn passes_basic_checks( +/// Does basic checks of a candidate. Provide the encoded PoV-block. Returns `Ok` if basic checks +/// are passed, `Err` otherwise. +fn perform_basic_checks( candidate: &CandidateDescriptor, max_block_data_size: Option<u64>, pov: &PoV, -) -> bool { +) -> Result<(), InvalidCandidate> { let encoded_pov = pov.encode(); let hash = pov.hash(); if let Some(max_size) = max_block_data_size { if encoded_pov.len() as u64 > max_size { - return false; + return Err(InvalidCandidate::ParamsTooLarge(encoded_pov.len() as u64)); } } if hash != candidate.pov_hash { - return false; + return Err(InvalidCandidate::HashMismatch); } if let Err(()) = candidate.check_collator_signature() { - return false; + return Err(InvalidCandidate::BadSignature); } - true + Ok(()) } /// Check the result of Wasm execution against the constraints given by the relay-chain. /// -/// Returns `true` if checks pass, false otherwise. +/// Returns `Ok(())` if checks pass, error otherwise. fn check_wasm_result_against_constraints( global_validation_data: &GlobalValidationData, _local_validation_data: &LocalValidationData, result: &WasmValidationResult, -) -> bool { +) -> Result<(), InvalidCandidate> { if result.head_data.0.len() > global_validation_data.max_head_data_size as _ { - return false + return Err(InvalidCandidate::HeadDataTooLarge(result.head_data.0.len() as u64)) } if let Some(ref code) = result.new_validation_code { if code.0.len() > global_validation_data.max_code_size as _ { - return false + return Err(InvalidCandidate::NewCodeTooLarge(code.0.len() as u64)) } } - true + Ok(()) } trait ValidationBackend { @@ -377,7 +378,7 @@ trait ValidationBackend { validation_code: &ValidationCode, params: ValidationParams, spawn: S, - ) -> Result<WasmValidationResult, wasm_executor::Error>; + ) -> Result<WasmValidationResult, ValidationError>; } struct RealValidationBackend; @@ -390,7 +391,7 @@ impl ValidationBackend for RealValidationBackend { validation_code: &ValidationCode, params: ValidationParams, spawn: S, - ) -> Result<WasmValidationResult, wasm_executor::Error> { + ) -> Result<WasmValidationResult, ValidationError> { let execution_mode = pool.as_ref() .map(ExecutionMode::Remote) .unwrap_or(ExecutionMode::Local); @@ -415,8 +416,8 @@ fn validate_candidate_exhaustive<B: ValidationBackend, S: SpawnNamed + 'static>( pov: Arc<PoV>, spawn: S, ) -> Result<ValidationResult, ValidationFailed> { - if !passes_basic_checks(&descriptor, None, &*pov) { - return Ok(ValidationResult::Invalid); + if let Err(e) = perform_basic_checks(&descriptor, None, &*pov) { + return Ok(ValidationResult::Invalid(e)) } let OmittedValidationData { global_validation, local_validation } = omitted_validation; @@ -431,26 +432,36 @@ fn validate_candidate_exhaustive<B: ValidationBackend, S: SpawnNamed + 'static>( }; match B::validate(backend_arg, &validation_code, params, spawn) { - Err(wasm_executor::Error::BadReturn) => Ok(ValidationResult::Invalid), - Err(_) => Err(ValidationFailed), + Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::Timeout)) => + Ok(ValidationResult::Invalid(InvalidCandidate::Timeout)), + Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::ParamsTooLarge(l))) => + Ok(ValidationResult::Invalid(InvalidCandidate::ParamsTooLarge(l as u64))), + Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::CodeTooLarge(l))) => + Ok(ValidationResult::Invalid(InvalidCandidate::CodeTooLarge(l as u64))), + Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::BadReturn)) => + Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn)), + Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::WasmExecutor(e))) => + Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(e.to_string()))), + Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::ExternalWasmExecutor(e))) => + Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(e.to_string()))), + Err(ValidationError::Internal(e)) => Err(ValidationFailed(e.to_string())), Ok(res) => { - let passes_post_checks = check_wasm_result_against_constraints( + let post_check_result = check_wasm_result_against_constraints( &global_validation, &local_validation, &res, ); - Ok(if passes_post_checks { - ValidationResult::Valid(ValidationOutputs { + Ok(match post_check_result { + Ok(()) => ValidationResult::Valid(ValidationOutputs { head_data: res.head_data, global_validation_data: global_validation, local_validation_data: local_validation, upward_messages: res.upward_messages, fees: 0, new_validation_code: res.new_validation_code, - }) - } else { - ValidationResult::Invalid + }), + Err(e) => ValidationResult::Invalid(e), }) } } @@ -469,7 +480,7 @@ mod tests { struct MockValidationBackend; struct MockValidationArg { - result: Result<WasmValidationResult, wasm_executor::Error>, + result: Result<WasmValidationResult, ValidationError>, } impl ValidationBackend for MockValidationBackend { @@ -480,7 +491,7 @@ mod tests { _validation_code: &ValidationCode, _params: ValidationParams, _spawn: S, - ) -> Result<WasmValidationResult, wasm_executor::Error> { + ) -> Result<WasmValidationResult, ValidationError> { arg.result } } @@ -795,7 +806,7 @@ mod tests { descriptor.pov_hash = pov.hash(); collator_sign(&mut descriptor, Sr25519Keyring::Alice); - assert!(passes_basic_checks(&descriptor, Some(1024), &pov)); + assert!(perform_basic_checks(&descriptor, Some(1024), &pov).is_ok()); let validation_result = WasmValidationResult { head_data: HeadData(vec![1, 1, 1]), @@ -808,7 +819,7 @@ mod tests { &omitted_validation.global_validation, &omitted_validation.local_validation, &validation_result, - )); + ).is_ok()); let v = validate_candidate_exhaustive::<MockValidationBackend, _>( MockValidationArg { result: Ok(validation_result) }, @@ -845,7 +856,7 @@ mod tests { descriptor.pov_hash = pov.hash(); collator_sign(&mut descriptor, Sr25519Keyring::Alice); - assert!(passes_basic_checks(&descriptor, Some(1024), &pov)); + assert!(perform_basic_checks(&descriptor, Some(1024), &pov).is_ok()); let validation_result = WasmValidationResult { head_data: HeadData(vec![1, 1, 1]), @@ -858,10 +869,14 @@ mod tests { &omitted_validation.global_validation, &omitted_validation.local_validation, &validation_result, - )); + ).is_ok()); let v = validate_candidate_exhaustive::<MockValidationBackend, _>( - MockValidationArg { result: Err(wasm_executor::Error::BadReturn) }, + MockValidationArg { + result: Err(ValidationError::InvalidCandidate( + WasmInvalidCandidate::BadReturn + )) + }, omitted_validation.clone(), vec![1, 2, 3].into(), descriptor, @@ -869,7 +884,7 @@ mod tests { TaskExecutor::new(), ).unwrap(); - assert_matches!(v, ValidationResult::Invalid); + assert_matches!(v, ValidationResult::Invalid(InvalidCandidate::BadReturn)); } @@ -889,7 +904,7 @@ mod tests { descriptor.pov_hash = pov.hash(); collator_sign(&mut descriptor, Sr25519Keyring::Alice); - assert!(passes_basic_checks(&descriptor, Some(1024), &pov)); + assert!(perform_basic_checks(&descriptor, Some(1024), &pov).is_ok()); let validation_result = WasmValidationResult { head_data: HeadData(vec![1, 1, 1]), @@ -902,10 +917,14 @@ mod tests { &omitted_validation.global_validation, &omitted_validation.local_validation, &validation_result, - )); + ).is_ok()); let v = validate_candidate_exhaustive::<MockValidationBackend, _>( - MockValidationArg { result: Err(wasm_executor::Error::Timeout) }, + MockValidationArg { + result: Err(ValidationError::InvalidCandidate( + WasmInvalidCandidate::Timeout + )) + }, omitted_validation.clone(), vec![1, 2, 3].into(), descriptor, @@ -913,6 +932,6 @@ mod tests { TaskExecutor::new(), ); - assert_matches!(v, Err(ValidationFailed)); + assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::Timeout))); } } diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index 5064a8515884a6a56db4b089f8bfc3d0638545a2..201522abb5065cff915b0db5170135bf7cbdc855 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -129,13 +129,38 @@ pub struct ValidationOutputs { pub new_validation_code: Option<ValidationCode>, } +/// Candidate invalidity details +#[derive(Debug)] +pub enum InvalidCandidate { + /// Failed to execute.`validate_block`. This includes function panicking. + ExecutionError(String), + /// Execution timeout. + Timeout, + /// Validation input is over the limit. + ParamsTooLarge(u64), + /// Code size is over the limit. + CodeTooLarge(u64), + /// Validation function returned invalid data. + BadReturn, + /// Invalid relay chain parent. + BadParent, + /// POV hash does not match. + HashMismatch, + /// Bad collator signature. + BadSignature, + /// Output code is too large + NewCodeTooLarge(u64), + /// Head-data is over the limit. + HeadDataTooLarge(u64), +} + /// Result of the validation of the candidate. #[derive(Debug)] pub enum ValidationResult { /// Candidate is valid. The validation process yields these outputs. Valid(ValidationOutputs), /// Candidate is invalid. - Invalid, + Invalid(InvalidCandidate), } impl std::convert::TryFrom<FromTableMisbehavior> for MisbehaviorReport { diff --git a/polkadot/node/subsystem/src/messages.rs b/polkadot/node/subsystem/src/messages.rs index d9a1f0393783d18018a76bd564b3288cd6d63f32..b508c34a8a90d8a0deea6b3af60f2af6c2fc4ad8 100644 --- a/polkadot/node/subsystem/src/messages.rs +++ b/polkadot/node/subsystem/src/messages.rs @@ -87,9 +87,9 @@ impl CandidateBackingMessage { } } -/// Blanket error for validation failing. +/// Blanket error for validation failing for internal reasons. #[derive(Debug)] -pub struct ValidationFailed; +pub struct ValidationFailed(pub String); /// Messages received by the Validation subsystem. /// diff --git a/polkadot/parachain/src/wasm_executor/mod.rs b/polkadot/parachain/src/wasm_executor/mod.rs index 584d2edb93035ecabc8c293b9194392703f69f40..7f53e7829c3b805111127f754b9a033611292ac6 100644 --- a/polkadot/parachain/src/wasm_executor/mod.rs +++ b/polkadot/parachain/src/wasm_executor/mod.rs @@ -69,9 +69,18 @@ pub enum ExecutionMode<'a> { RemoteTest(&'a ValidationPool), } -/// Error type for the wasm executor #[derive(Debug, derive_more::Display, derive_more::From)] -pub enum Error { +/// Candidate validation error. +pub enum ValidationError { + /// Validation failed due to internal reasons. The candidate might still be valid. + Internal(InternalError), + /// Candidate is invalid. + InvalidCandidate(InvalidCandidate), +} + +/// Error type that indicates invalid candidate. +#[derive(Debug, derive_more::Display, derive_more::From)] +pub enum InvalidCandidate { /// Wasm executor error. #[display(fmt = "WASM executor error: {:?}", _0)] WasmExecutor(sc_executor::error::Error), @@ -82,30 +91,37 @@ pub enum Error { /// Code size it too large. #[display(fmt = "WASM code is {} bytes, max allowed is {}", _0, MAX_CODE_MEM)] CodeTooLarge(usize), - /// Bad return data or type. + /// Error decoding returned data. #[display(fmt = "Validation function returned invalid data.")] BadReturn, #[display(fmt = "Validation function timeout.")] Timeout, + #[display(fmt = "External WASM execution error: {}", _0)] + ExternalWasmExecutor(String), +} + +/// Host error during candidate validation. This does not indicate an invalid candidate. +#[derive(Debug, derive_more::Display, derive_more::From)] +pub enum InternalError { #[display(fmt = "IO error: {}", _0)] Io(std::io::Error), #[display(fmt = "System error: {}", _0)] System(Box<dyn std::error::Error + Send>), - #[display(fmt = "WASM worker error: {}", _0)] - External(String), #[display(fmt = "Shared memory error: {}", _0)] #[cfg(not(any(target_os = "android", target_os = "unknown")))] SharedMem(shared_memory::SharedMemError), + #[display(fmt = "WASM worker error: {}", _0)] + WasmWorker(String), } -impl std::error::Error for Error { +impl std::error::Error for ValidationError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - Error::WasmExecutor(ref err) => Some(err), - Error::Io(ref err) => Some(err), - Error::System(ref err) => Some(&**err), + ValidationError::Internal(InternalError::Io(ref err)) => Some(err), + ValidationError::Internal(InternalError::System(ref err)) => Some(&**err), #[cfg(not(any(target_os = "android", target_os = "unknown")))] - Error::SharedMem(ref err) => Some(err), + ValidationError::Internal(InternalError::SharedMem(ref err)) => Some(err), + ValidationError::InvalidCandidate(InvalidCandidate::WasmExecutor(ref err)) => Some(err), _ => None, } } @@ -119,7 +135,7 @@ pub fn validate_candidate( params: ValidationParams, options: ExecutionMode<'_>, spawner: impl SpawnNamed + 'static, -) -> Result<ValidationResult, Error> { +) -> Result<ValidationResult, ValidationError> { match options { ExecutionMode::Local => { validate_candidate_internal(validation_code, ¶ms.encode(), spawner) @@ -133,15 +149,19 @@ pub fn validate_candidate( pool.validate_candidate(validation_code, params, true) }, #[cfg(any(target_os = "android", target_os = "unknown"))] - ExecutionMode::Remote(pool) => - Err(Error::System(Box::<dyn std::error::Error + Send + Sync>::from( - "Remote validator not available".to_string() - ) as Box<_>)), + ExecutionMode::Remote(_pool) => + Err(ValidationError::Internal(InternalError::System( + Box::<dyn std::error::Error + Send + Sync>::from( + "Remote validator not available".to_string() + ) as Box<_> + ))), #[cfg(any(target_os = "android", target_os = "unknown"))] - ExecutionMode::RemoteTest(pool) => - Err(Error::System(Box::<dyn std::error::Error + Send + Sync>::from( - "Remote validator not available".to_string() - ) as Box<_>)), + ExecutionMode::RemoteTest(_pool) => + Err(ValidationError::Internal(InternalError::System( + Box::<dyn std::error::Error + Send + Sync>::from( + "Remote validator not available".to_string() + ) as Box<_> + ))), } } @@ -155,7 +175,7 @@ pub fn validate_candidate_internal( validation_code: &[u8], encoded_call_data: &[u8], spawner: impl SpawnNamed + 'static, -) -> Result<ValidationResult, Error> { +) -> Result<ValidationResult, ValidationError> { let mut extensions = Extensions::new(); extensions.register(sp_core::traits::TaskExecutorExt::new(spawner)); @@ -175,9 +195,10 @@ pub fn validate_candidate_internal( encoded_call_data, &mut ext, sp_core::traits::MissingHostFunctions::Allow, - )?; + ).map_err(|e| ValidationError::InvalidCandidate(e.into()))?; - ValidationResult::decode(&mut &res[..]).map_err(|_| Error::BadReturn.into()) + ValidationResult::decode(&mut &res[..]) + .map_err(|_| ValidationError::InvalidCandidate(InvalidCandidate::BadReturn).into()) } /// The validation externalities that will panic on any storage related access. They just provide diff --git a/polkadot/parachain/src/wasm_executor/validation_host.rs b/polkadot/parachain/src/wasm_executor/validation_host.rs index 96ad7d9ebc4c6022e611b93ab6fb4efc660ebc5c..ad79949d21911eb76772ed2d147974293029d003 100644 --- a/polkadot/parachain/src/wasm_executor/validation_host.rs +++ b/polkadot/parachain/src/wasm_executor/validation_host.rs @@ -19,7 +19,8 @@ use std::{process, env, sync::Arc, sync::atomic}; use codec::{Decode, Encode}; use crate::primitives::{ValidationParams, ValidationResult}; -use super::{validate_candidate_internal, Error, MAX_CODE_MEM, MAX_RUNTIME_MEM}; +use super::{validate_candidate_internal, ValidationError, InvalidCandidate, InternalError, + MAX_CODE_MEM, MAX_RUNTIME_MEM}; use shared_memory::{SharedMem, SharedMemConf, EventState, WriteLockable, EventWait, EventSet}; use parking_lot::Mutex; use log::{debug, trace}; @@ -88,7 +89,7 @@ impl ValidationPool { validation_code: &[u8], params: ValidationParams, test_mode: bool, - ) -> Result<ValidationResult, Error> { + ) -> Result<ValidationResult, ValidationError> { for host in self.hosts.iter() { if let Some(mut host) = host.try_lock() { return host.validate_candidate(validation_code, params, test_mode); @@ -165,7 +166,10 @@ pub fn run_worker(mem_id: &str) -> Result<(), String> { match result { Ok(r) => ValidationResultHeader::Ok(r), - Err(e) => ValidationResultHeader::Error(e.to_string()), + Err(ValidationError::Internal(e)) => + ValidationResultHeader::Error(WorkerValidationError::InternalError(e.to_string())), + Err(ValidationError::InvalidCandidate(e)) => + ValidationResultHeader::Error(WorkerValidationError::ValidationError(e.to_string())), } }; let mut data: &mut[u8] = &mut **slice; @@ -186,9 +190,15 @@ struct ValidationHeader { } #[derive(Encode, Decode, Debug)] -pub enum ValidationResultHeader { +enum WorkerValidationError { + InternalError(String), + ValidationError(String), +} + +#[derive(Encode, Decode, Debug)] +enum ValidationResultHeader { Ok(ValidationResult), - Error(String), + Error(WorkerValidationError), } unsafe impl Send for ValidationHost {} @@ -209,7 +219,7 @@ impl Drop for ValidationHost { } impl ValidationHost { - fn create_memory() -> Result<SharedMem, Error> { + fn create_memory() -> Result<SharedMem, InternalError> { let mem_size = MAX_RUNTIME_MEM + MAX_CODE_MEM + 1024; let mem_config = SharedMemConf::default() .set_size(mem_size) @@ -221,7 +231,7 @@ impl ValidationHost { Ok(mem_config.create()?) } - fn start_worker(&mut self, test_mode: bool) -> Result<(), Error> { + fn start_worker(&mut self, test_mode: bool) -> Result<(), InternalError> { if let Some(ref mut worker) = self.worker { // Check if still alive if let Ok(None) = worker.try_wait() { @@ -257,9 +267,9 @@ impl ValidationHost { validation_code: &[u8], params: ValidationParams, test_mode: bool, - ) -> Result<ValidationResult, Error> { + ) -> Result<ValidationResult, ValidationError> { if validation_code.len() > MAX_CODE_MEM { - return Err(Error::CodeTooLarge(validation_code.len())); + return Err(ValidationError::InvalidCandidate(InvalidCandidate::CodeTooLarge(validation_code.len()))); } // First, check if need to spawn the child process self.start_worker(test_mode)?; @@ -267,7 +277,8 @@ impl ValidationHost { .expect("memory is always `Some` after `start_worker` completes successfully"); { // Put data in shared mem - let data: &mut[u8] = &mut **memory.wlock_as_slice(0)?; + let data: &mut[u8] = &mut **memory.wlock_as_slice(0) + .map_err(|e|ValidationError::Internal(e.into()))?; let (mut header_buf, rest) = data.split_at_mut(1024); let (code, rest) = rest.split_at_mut(MAX_CODE_MEM); let (code, _) = code.split_at_mut(validation_code.len()); @@ -275,7 +286,7 @@ impl ValidationHost { code[..validation_code.len()].copy_from_slice(validation_code); let encoded_params = params.encode(); if encoded_params.len() >= MAX_RUNTIME_MEM { - return Err(Error::ParamsTooLarge(MAX_RUNTIME_MEM)); + return Err(ValidationError::InvalidCandidate(InvalidCandidate::ParamsTooLarge(MAX_RUNTIME_MEM))); } call_data[..encoded_params.len()].copy_from_slice(&encoded_params); @@ -288,7 +299,8 @@ impl ValidationHost { } debug!("{} Signaling candidate", self.id); - memory.set(Event::CandidateReady as usize, EventState::Signaled)?; + memory.set(Event::CandidateReady as usize, EventState::Signaled) + .map_err(|e| ValidationError::Internal(e.into()))?; debug!("{} Waiting for results", self.id); match memory.wait(Event::ResultReady as usize, shared_memory::Timeout::Sec(EXECUTION_TIMEOUT_SEC as usize)) { @@ -297,22 +309,27 @@ impl ValidationHost { if let Some(mut worker) = self.worker.take() { worker.kill().ok(); } - return Err(Error::Timeout.into()); + return Err(ValidationError::InvalidCandidate(InvalidCandidate::Timeout)); } Ok(()) => {} } { debug!("{} Reading results", self.id); - let data: &[u8] = &**memory.wlock_as_slice(0)?; + let data: &[u8] = &**memory.wlock_as_slice(0) + .map_err(|e| ValidationError::Internal(e.into()))?; let (header_buf, _) = data.split_at(1024); let mut header_buf: &[u8] = header_buf; let header = ValidationResultHeader::decode(&mut header_buf).unwrap(); match header { ValidationResultHeader::Ok(result) => Ok(result), - ValidationResultHeader::Error(message) => { - debug!("{} Validation error: {}", self.id, message); - Err(Error::External(message).into()) + ValidationResultHeader::Error(WorkerValidationError::InternalError(e)) => { + debug!("{} Internal validation error: {}", self.id, e); + Err(ValidationError::Internal(InternalError::WasmWorker(e))) + }, + ValidationResultHeader::Error(WorkerValidationError::ValidationError(e)) => { + debug!("{} External validation error: {}", self.id, e); + Err(ValidationError::InvalidCandidate(InvalidCandidate::ExternalWasmExecutor(e))) } } } diff --git a/polkadot/parachain/test-parachains/tests/wasm_executor/mod.rs b/polkadot/parachain/test-parachains/tests/wasm_executor/mod.rs index 0e696b2395cfacfcf270ebc0825caec7b47fbfba..769ad737ce9f4c34574ffe0a33ea2aae24339599 100644 --- a/polkadot/parachain/test-parachains/tests/wasm_executor/mod.rs +++ b/polkadot/parachain/test-parachains/tests/wasm_executor/mod.rs @@ -19,7 +19,7 @@ use crate::adder; use parachain::{ primitives::{BlockData, ValidationParams}, - wasm_executor::EXECUTION_TIMEOUT_SEC, + wasm_executor::{ValidationError, InvalidCandidate, EXECUTION_TIMEOUT_SEC}, }; #[test] @@ -40,7 +40,7 @@ fn terminates_on_timeout() { sp_core::testing::TaskExecutor::new(), ); match result { - Err(parachain::wasm_executor::Error::Timeout) => {}, + Err(ValidationError::InvalidCandidate(InvalidCandidate::Timeout)) => {}, r => panic!("{:?}", r), } diff --git a/polkadot/validation/src/error.rs b/polkadot/validation/src/error.rs index 5fd990a0713327469277910242d7a48c9375a2a4..d4632a4782fb9b170d6c2658e3a3a6abf8f8344b 100644 --- a/polkadot/validation/src/error.rs +++ b/polkadot/validation/src/error.rs @@ -26,7 +26,7 @@ pub enum Error { /// Consensus error Consensus(consensus::error::Error), /// A wasm-validation error. - WasmValidation(parachain::wasm_executor::Error), + WasmValidation(parachain::wasm_executor::ValidationError), /// An I/O error. Io(std::io::Error), /// An error in the availability erasure-coding.