Sort out validation errors (#1516)

* Sort out validation errors

* Typo

* Fixed wasm/android build

* Fixed bad merge
This commit is contained in:
Arkadiy Paronyan
2020-08-03 12:45:26 +02:00
committed by GitHub
parent 277fd75179
commit 09ce64bf24
8 changed files with 176 additions and 93 deletions
+6 -5
View File
@@ -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. // 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 // there's an infinite amount of garbage out there. no need to acknowledge
@@ -497,7 +497,7 @@ impl CandidateBackingJob {
Err(()) => Statement::Invalid(candidate_hash), Err(()) => Statement::Invalid(candidate_hash),
} }
} }
ValidationResult::Invalid => { ValidationResult::Invalid(_reason) => {
Statement::Invalid(candidate_hash) Statement::Invalid(candidate_hash)
} }
}; };
@@ -826,6 +826,7 @@ mod tests {
messages::RuntimeApiRequest, messages::RuntimeApiRequest,
ActiveLeavesUpdate, FromOverseer, OverseerSignal, ActiveLeavesUpdate, FromOverseer, OverseerSignal,
}; };
use polkadot_node_primitives::InvalidCandidate;
use sp_keyring::Sr25519Keyring; use sp_keyring::Sr25519Keyring;
use std::collections::HashMap; use std::collections::HashMap;
@@ -1461,7 +1462,7 @@ mod tests {
tx, tx,
) )
) if pov == pov && &c == candidate_a.descriptor() => { ) 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, tx,
) )
) if pov == pov && &c == candidate.descriptor() => { ) 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, tx,
) )
) if pov == pov && &c == candidate.descriptor() => { ) if pov == pov && &c == candidate.descriptor() => {
tx.send(Err(ValidationFailed)).unwrap(); tx.send(Err(ValidationFailed("Internal test error".into()))).unwrap();
} }
); );
@@ -28,12 +28,13 @@ use polkadot_subsystem::messages::{
AllMessages, CandidateValidationMessage, RuntimeApiMessage, ValidationFailed, RuntimeApiRequest, AllMessages, CandidateValidationMessage, RuntimeApiMessage, ValidationFailed, RuntimeApiRequest,
}; };
use polkadot_subsystem::errors::RuntimeApiError; use polkadot_subsystem::errors::RuntimeApiError;
use polkadot_node_primitives::{ValidationResult, ValidationOutputs}; use polkadot_node_primitives::{ValidationResult, ValidationOutputs, InvalidCandidate};
use polkadot_primitives::v1::{ use polkadot_primitives::v1::{
ValidationCode, OmittedValidationData, PoV, CandidateDescriptor, LocalValidationData, ValidationCode, OmittedValidationData, PoV, CandidateDescriptor, LocalValidationData,
GlobalValidationData, OccupiedCoreAssumption, Hash, validation_data_hash, 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 polkadot_parachain::primitives::{ValidationResult as WasmValidationResult, ValidationParams};
use parity_scale_codec::Encode; use parity_scale_codec::Encode;
@@ -241,7 +242,7 @@ async fn spawn_validate_from_chain_state(
e, 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; ).await;
} }
AssumptionCheckOutcome::DoesNotMatch => {}, AssumptionCheckOutcome::DoesNotMatch => {},
AssumptionCheckOutcome::BadRequest => return Ok(Err(ValidationFailed)), AssumptionCheckOutcome::BadRequest => return Ok(Err(ValidationFailed("Bad request".into()))),
} }
match check_assumption_validation_data( match check_assumption_validation_data(
@@ -285,13 +286,13 @@ async fn spawn_validate_from_chain_state(
).await; ).await;
} }
AssumptionCheckOutcome::DoesNotMatch => {}, 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 // 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 // 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. // 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( async fn spawn_validate_exhaustive(
@@ -321,52 +322,52 @@ async fn spawn_validate_exhaustive(
rx.await.map_err(Into::into) rx.await.map_err(Into::into)
} }
/// Does basic checks of a candidate. Provide the encoded PoV-block. Returns `true` if basic checks /// Does basic checks of a candidate. Provide the encoded PoV-block. Returns `Ok` if basic checks
/// are passed, false otherwise. /// are passed, `Err` otherwise.
fn passes_basic_checks( fn perform_basic_checks(
candidate: &CandidateDescriptor, candidate: &CandidateDescriptor,
max_block_data_size: Option<u64>, max_block_data_size: Option<u64>,
pov: &PoV, pov: &PoV,
) -> bool { ) -> Result<(), InvalidCandidate> {
let encoded_pov = pov.encode(); let encoded_pov = pov.encode();
let hash = pov.hash(); let hash = pov.hash();
if let Some(max_size) = max_block_data_size { if let Some(max_size) = max_block_data_size {
if encoded_pov.len() as u64 > max_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 { if hash != candidate.pov_hash {
return false; return Err(InvalidCandidate::HashMismatch);
} }
if let Err(()) = candidate.check_collator_signature() { 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. /// 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( fn check_wasm_result_against_constraints(
global_validation_data: &GlobalValidationData, global_validation_data: &GlobalValidationData,
_local_validation_data: &LocalValidationData, _local_validation_data: &LocalValidationData,
result: &WasmValidationResult, result: &WasmValidationResult,
) -> bool { ) -> Result<(), InvalidCandidate> {
if result.head_data.0.len() > global_validation_data.max_head_data_size as _ { 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 let Some(ref code) = result.new_validation_code {
if code.0.len() > global_validation_data.max_code_size as _ { 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 { trait ValidationBackend {
@@ -377,7 +378,7 @@ trait ValidationBackend {
validation_code: &ValidationCode, validation_code: &ValidationCode,
params: ValidationParams, params: ValidationParams,
spawn: S, spawn: S,
) -> Result<WasmValidationResult, wasm_executor::Error>; ) -> Result<WasmValidationResult, ValidationError>;
} }
struct RealValidationBackend; struct RealValidationBackend;
@@ -390,7 +391,7 @@ impl ValidationBackend for RealValidationBackend {
validation_code: &ValidationCode, validation_code: &ValidationCode,
params: ValidationParams, params: ValidationParams,
spawn: S, spawn: S,
) -> Result<WasmValidationResult, wasm_executor::Error> { ) -> Result<WasmValidationResult, ValidationError> {
let execution_mode = pool.as_ref() let execution_mode = pool.as_ref()
.map(ExecutionMode::Remote) .map(ExecutionMode::Remote)
.unwrap_or(ExecutionMode::Local); .unwrap_or(ExecutionMode::Local);
@@ -415,8 +416,8 @@ fn validate_candidate_exhaustive<B: ValidationBackend, S: SpawnNamed + 'static>(
pov: Arc<PoV>, pov: Arc<PoV>,
spawn: S, spawn: S,
) -> Result<ValidationResult, ValidationFailed> { ) -> Result<ValidationResult, ValidationFailed> {
if !passes_basic_checks(&descriptor, None, &*pov) { if let Err(e) = perform_basic_checks(&descriptor, None, &*pov) {
return Ok(ValidationResult::Invalid); return Ok(ValidationResult::Invalid(e))
} }
let OmittedValidationData { global_validation, local_validation } = omitted_validation; 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) { match B::validate(backend_arg, &validation_code, params, spawn) {
Err(wasm_executor::Error::BadReturn) => Ok(ValidationResult::Invalid), Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::Timeout)) =>
Err(_) => Err(ValidationFailed), 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) => { Ok(res) => {
let passes_post_checks = check_wasm_result_against_constraints( let post_check_result = check_wasm_result_against_constraints(
&global_validation, &global_validation,
&local_validation, &local_validation,
&res, &res,
); );
Ok(if passes_post_checks { Ok(match post_check_result {
ValidationResult::Valid(ValidationOutputs { Ok(()) => ValidationResult::Valid(ValidationOutputs {
head_data: res.head_data, head_data: res.head_data,
global_validation_data: global_validation, global_validation_data: global_validation,
local_validation_data: local_validation, local_validation_data: local_validation,
upward_messages: res.upward_messages, upward_messages: res.upward_messages,
fees: 0, fees: 0,
new_validation_code: res.new_validation_code, new_validation_code: res.new_validation_code,
}) }),
} else { Err(e) => ValidationResult::Invalid(e),
ValidationResult::Invalid
}) })
} }
} }
@@ -469,7 +480,7 @@ mod tests {
struct MockValidationBackend; struct MockValidationBackend;
struct MockValidationArg { struct MockValidationArg {
result: Result<WasmValidationResult, wasm_executor::Error>, result: Result<WasmValidationResult, ValidationError>,
} }
impl ValidationBackend for MockValidationBackend { impl ValidationBackend for MockValidationBackend {
@@ -480,7 +491,7 @@ mod tests {
_validation_code: &ValidationCode, _validation_code: &ValidationCode,
_params: ValidationParams, _params: ValidationParams,
_spawn: S, _spawn: S,
) -> Result<WasmValidationResult, wasm_executor::Error> { ) -> Result<WasmValidationResult, ValidationError> {
arg.result arg.result
} }
} }
@@ -795,7 +806,7 @@ mod tests {
descriptor.pov_hash = pov.hash(); descriptor.pov_hash = pov.hash();
collator_sign(&mut descriptor, Sr25519Keyring::Alice); 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 { let validation_result = WasmValidationResult {
head_data: HeadData(vec![1, 1, 1]), head_data: HeadData(vec![1, 1, 1]),
@@ -808,7 +819,7 @@ mod tests {
&omitted_validation.global_validation, &omitted_validation.global_validation,
&omitted_validation.local_validation, &omitted_validation.local_validation,
&validation_result, &validation_result,
)); ).is_ok());
let v = validate_candidate_exhaustive::<MockValidationBackend, _>( let v = validate_candidate_exhaustive::<MockValidationBackend, _>(
MockValidationArg { result: Ok(validation_result) }, MockValidationArg { result: Ok(validation_result) },
@@ -845,7 +856,7 @@ mod tests {
descriptor.pov_hash = pov.hash(); descriptor.pov_hash = pov.hash();
collator_sign(&mut descriptor, Sr25519Keyring::Alice); 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 { let validation_result = WasmValidationResult {
head_data: HeadData(vec![1, 1, 1]), head_data: HeadData(vec![1, 1, 1]),
@@ -858,10 +869,14 @@ mod tests {
&omitted_validation.global_validation, &omitted_validation.global_validation,
&omitted_validation.local_validation, &omitted_validation.local_validation,
&validation_result, &validation_result,
)); ).is_ok());
let v = validate_candidate_exhaustive::<MockValidationBackend, _>( let v = validate_candidate_exhaustive::<MockValidationBackend, _>(
MockValidationArg { result: Err(wasm_executor::Error::BadReturn) }, MockValidationArg {
result: Err(ValidationError::InvalidCandidate(
WasmInvalidCandidate::BadReturn
))
},
omitted_validation.clone(), omitted_validation.clone(),
vec![1, 2, 3].into(), vec![1, 2, 3].into(),
descriptor, descriptor,
@@ -869,7 +884,7 @@ mod tests {
TaskExecutor::new(), TaskExecutor::new(),
).unwrap(); ).unwrap();
assert_matches!(v, ValidationResult::Invalid); assert_matches!(v, ValidationResult::Invalid(InvalidCandidate::BadReturn));
} }
@@ -889,7 +904,7 @@ mod tests {
descriptor.pov_hash = pov.hash(); descriptor.pov_hash = pov.hash();
collator_sign(&mut descriptor, Sr25519Keyring::Alice); 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 { let validation_result = WasmValidationResult {
head_data: HeadData(vec![1, 1, 1]), head_data: HeadData(vec![1, 1, 1]),
@@ -902,10 +917,14 @@ mod tests {
&omitted_validation.global_validation, &omitted_validation.global_validation,
&omitted_validation.local_validation, &omitted_validation.local_validation,
&validation_result, &validation_result,
)); ).is_ok());
let v = validate_candidate_exhaustive::<MockValidationBackend, _>( let v = validate_candidate_exhaustive::<MockValidationBackend, _>(
MockValidationArg { result: Err(wasm_executor::Error::Timeout) }, MockValidationArg {
result: Err(ValidationError::InvalidCandidate(
WasmInvalidCandidate::Timeout
))
},
omitted_validation.clone(), omitted_validation.clone(),
vec![1, 2, 3].into(), vec![1, 2, 3].into(),
descriptor, descriptor,
@@ -913,6 +932,6 @@ mod tests {
TaskExecutor::new(), TaskExecutor::new(),
); );
assert_matches!(v, Err(ValidationFailed)); assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::Timeout)));
} }
} }
+26 -1
View File
@@ -129,13 +129,38 @@ pub struct ValidationOutputs {
pub new_validation_code: Option<ValidationCode>, 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. /// Result of the validation of the candidate.
#[derive(Debug)] #[derive(Debug)]
pub enum ValidationResult { pub enum ValidationResult {
/// Candidate is valid. The validation process yields these outputs. /// Candidate is valid. The validation process yields these outputs.
Valid(ValidationOutputs), Valid(ValidationOutputs),
/// Candidate is invalid. /// Candidate is invalid.
Invalid, Invalid(InvalidCandidate),
} }
impl std::convert::TryFrom<FromTableMisbehavior> for MisbehaviorReport { impl std::convert::TryFrom<FromTableMisbehavior> for MisbehaviorReport {
+2 -2
View File
@@ -87,9 +87,9 @@ impl CandidateBackingMessage {
} }
} }
/// Blanket error for validation failing. /// Blanket error for validation failing for internal reasons.
#[derive(Debug)] #[derive(Debug)]
pub struct ValidationFailed; pub struct ValidationFailed(pub String);
/// Messages received by the Validation subsystem. /// Messages received by the Validation subsystem.
/// ///
+41 -20
View File
@@ -69,9 +69,18 @@ pub enum ExecutionMode<'a> {
RemoteTest(&'a ValidationPool), RemoteTest(&'a ValidationPool),
} }
/// Error type for the wasm executor
#[derive(Debug, derive_more::Display, derive_more::From)] #[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. /// Wasm executor error.
#[display(fmt = "WASM executor error: {:?}", _0)] #[display(fmt = "WASM executor error: {:?}", _0)]
WasmExecutor(sc_executor::error::Error), WasmExecutor(sc_executor::error::Error),
@@ -82,30 +91,37 @@ pub enum Error {
/// Code size it too large. /// Code size it too large.
#[display(fmt = "WASM code is {} bytes, max allowed is {}", _0, MAX_CODE_MEM)] #[display(fmt = "WASM code is {} bytes, max allowed is {}", _0, MAX_CODE_MEM)]
CodeTooLarge(usize), CodeTooLarge(usize),
/// Bad return data or type. /// Error decoding returned data.
#[display(fmt = "Validation function returned invalid data.")] #[display(fmt = "Validation function returned invalid data.")]
BadReturn, BadReturn,
#[display(fmt = "Validation function timeout.")] #[display(fmt = "Validation function timeout.")]
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)] #[display(fmt = "IO error: {}", _0)]
Io(std::io::Error), Io(std::io::Error),
#[display(fmt = "System error: {}", _0)] #[display(fmt = "System error: {}", _0)]
System(Box<dyn std::error::Error + Send>), System(Box<dyn std::error::Error + Send>),
#[display(fmt = "WASM worker error: {}", _0)]
External(String),
#[display(fmt = "Shared memory error: {}", _0)] #[display(fmt = "Shared memory error: {}", _0)]
#[cfg(not(any(target_os = "android", target_os = "unknown")))] #[cfg(not(any(target_os = "android", target_os = "unknown")))]
SharedMem(shared_memory::SharedMemError), 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)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self { match self {
Error::WasmExecutor(ref err) => Some(err), ValidationError::Internal(InternalError::Io(ref err)) => Some(err),
Error::Io(ref err) => Some(err), ValidationError::Internal(InternalError::System(ref err)) => Some(&**err),
Error::System(ref err) => Some(&**err),
#[cfg(not(any(target_os = "android", target_os = "unknown")))] #[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, _ => None,
} }
} }
@@ -119,7 +135,7 @@ pub fn validate_candidate(
params: ValidationParams, params: ValidationParams,
options: ExecutionMode<'_>, options: ExecutionMode<'_>,
spawner: impl SpawnNamed + 'static, spawner: impl SpawnNamed + 'static,
) -> Result<ValidationResult, Error> { ) -> Result<ValidationResult, ValidationError> {
match options { match options {
ExecutionMode::Local => { ExecutionMode::Local => {
validate_candidate_internal(validation_code, &params.encode(), spawner) validate_candidate_internal(validation_code, &params.encode(), spawner)
@@ -133,15 +149,19 @@ pub fn validate_candidate(
pool.validate_candidate(validation_code, params, true) pool.validate_candidate(validation_code, params, true)
}, },
#[cfg(any(target_os = "android", target_os = "unknown"))] #[cfg(any(target_os = "android", target_os = "unknown"))]
ExecutionMode::Remote(pool) => ExecutionMode::Remote(_pool) =>
Err(Error::System(Box::<dyn std::error::Error + Send + Sync>::from( Err(ValidationError::Internal(InternalError::System(
Box::<dyn std::error::Error + Send + Sync>::from(
"Remote validator not available".to_string() "Remote validator not available".to_string()
) as Box<_>)), ) as Box<_>
))),
#[cfg(any(target_os = "android", target_os = "unknown"))] #[cfg(any(target_os = "android", target_os = "unknown"))]
ExecutionMode::RemoteTest(pool) => ExecutionMode::RemoteTest(_pool) =>
Err(Error::System(Box::<dyn std::error::Error + Send + Sync>::from( Err(ValidationError::Internal(InternalError::System(
Box::<dyn std::error::Error + Send + Sync>::from(
"Remote validator not available".to_string() "Remote validator not available".to_string()
) as Box<_>)), ) as Box<_>
))),
} }
} }
@@ -155,7 +175,7 @@ pub fn validate_candidate_internal(
validation_code: &[u8], validation_code: &[u8],
encoded_call_data: &[u8], encoded_call_data: &[u8],
spawner: impl SpawnNamed + 'static, spawner: impl SpawnNamed + 'static,
) -> Result<ValidationResult, Error> { ) -> Result<ValidationResult, ValidationError> {
let mut extensions = Extensions::new(); let mut extensions = Extensions::new();
extensions.register(sp_core::traits::TaskExecutorExt::new(spawner)); extensions.register(sp_core::traits::TaskExecutorExt::new(spawner));
@@ -175,9 +195,10 @@ pub fn validate_candidate_internal(
encoded_call_data, encoded_call_data,
&mut ext, &mut ext,
sp_core::traits::MissingHostFunctions::Allow, 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 /// The validation externalities that will panic on any storage related access. They just provide
@@ -19,7 +19,8 @@
use std::{process, env, sync::Arc, sync::atomic}; use std::{process, env, sync::Arc, sync::atomic};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use crate::primitives::{ValidationParams, ValidationResult}; 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 shared_memory::{SharedMem, SharedMemConf, EventState, WriteLockable, EventWait, EventSet};
use parking_lot::Mutex; use parking_lot::Mutex;
use log::{debug, trace}; use log::{debug, trace};
@@ -88,7 +89,7 @@ impl ValidationPool {
validation_code: &[u8], validation_code: &[u8],
params: ValidationParams, params: ValidationParams,
test_mode: bool, test_mode: bool,
) -> Result<ValidationResult, Error> { ) -> Result<ValidationResult, ValidationError> {
for host in self.hosts.iter() { for host in self.hosts.iter() {
if let Some(mut host) = host.try_lock() { if let Some(mut host) = host.try_lock() {
return host.validate_candidate(validation_code, params, test_mode); return host.validate_candidate(validation_code, params, test_mode);
@@ -165,7 +166,10 @@ pub fn run_worker(mem_id: &str) -> Result<(), String> {
match result { match result {
Ok(r) => ValidationResultHeader::Ok(r), 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; let mut data: &mut[u8] = &mut **slice;
@@ -186,9 +190,15 @@ struct ValidationHeader {
} }
#[derive(Encode, Decode, Debug)] #[derive(Encode, Decode, Debug)]
pub enum ValidationResultHeader { enum WorkerValidationError {
InternalError(String),
ValidationError(String),
}
#[derive(Encode, Decode, Debug)]
enum ValidationResultHeader {
Ok(ValidationResult), Ok(ValidationResult),
Error(String), Error(WorkerValidationError),
} }
unsafe impl Send for ValidationHost {} unsafe impl Send for ValidationHost {}
@@ -209,7 +219,7 @@ impl Drop for ValidationHost {
} }
impl 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_size = MAX_RUNTIME_MEM + MAX_CODE_MEM + 1024;
let mem_config = SharedMemConf::default() let mem_config = SharedMemConf::default()
.set_size(mem_size) .set_size(mem_size)
@@ -221,7 +231,7 @@ impl ValidationHost {
Ok(mem_config.create()?) 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 { if let Some(ref mut worker) = self.worker {
// Check if still alive // Check if still alive
if let Ok(None) = worker.try_wait() { if let Ok(None) = worker.try_wait() {
@@ -257,9 +267,9 @@ impl ValidationHost {
validation_code: &[u8], validation_code: &[u8],
params: ValidationParams, params: ValidationParams,
test_mode: bool, test_mode: bool,
) -> Result<ValidationResult, Error> { ) -> Result<ValidationResult, ValidationError> {
if validation_code.len() > MAX_CODE_MEM { 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 // First, check if need to spawn the child process
self.start_worker(test_mode)?; self.start_worker(test_mode)?;
@@ -267,7 +277,8 @@ impl ValidationHost {
.expect("memory is always `Some` after `start_worker` completes successfully"); .expect("memory is always `Some` after `start_worker` completes successfully");
{ {
// Put data in shared mem // 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 (mut header_buf, rest) = data.split_at_mut(1024);
let (code, rest) = rest.split_at_mut(MAX_CODE_MEM); let (code, rest) = rest.split_at_mut(MAX_CODE_MEM);
let (code, _) = code.split_at_mut(validation_code.len()); let (code, _) = code.split_at_mut(validation_code.len());
@@ -275,7 +286,7 @@ impl ValidationHost {
code[..validation_code.len()].copy_from_slice(validation_code); code[..validation_code.len()].copy_from_slice(validation_code);
let encoded_params = params.encode(); let encoded_params = params.encode();
if encoded_params.len() >= MAX_RUNTIME_MEM { 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); call_data[..encoded_params.len()].copy_from_slice(&encoded_params);
@@ -288,7 +299,8 @@ impl ValidationHost {
} }
debug!("{} Signaling candidate", self.id); 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); debug!("{} Waiting for results", self.id);
match memory.wait(Event::ResultReady as usize, shared_memory::Timeout::Sec(EXECUTION_TIMEOUT_SEC as usize)) { 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() { if let Some(mut worker) = self.worker.take() {
worker.kill().ok(); worker.kill().ok();
} }
return Err(Error::Timeout.into()); return Err(ValidationError::InvalidCandidate(InvalidCandidate::Timeout));
} }
Ok(()) => {} Ok(()) => {}
} }
{ {
debug!("{} Reading results", self.id); 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 (header_buf, _) = data.split_at(1024);
let mut header_buf: &[u8] = header_buf; let mut header_buf: &[u8] = header_buf;
let header = ValidationResultHeader::decode(&mut header_buf).unwrap(); let header = ValidationResultHeader::decode(&mut header_buf).unwrap();
match header { match header {
ValidationResultHeader::Ok(result) => Ok(result), ValidationResultHeader::Ok(result) => Ok(result),
ValidationResultHeader::Error(message) => { ValidationResultHeader::Error(WorkerValidationError::InternalError(e)) => {
debug!("{} Validation error: {}", self.id, message); debug!("{} Internal validation error: {}", self.id, e);
Err(Error::External(message).into()) Err(ValidationError::Internal(InternalError::WasmWorker(e)))
},
ValidationResultHeader::Error(WorkerValidationError::ValidationError(e)) => {
debug!("{} External validation error: {}", self.id, e);
Err(ValidationError::InvalidCandidate(InvalidCandidate::ExternalWasmExecutor(e)))
} }
} }
} }
@@ -19,7 +19,7 @@
use crate::adder; use crate::adder;
use parachain::{ use parachain::{
primitives::{BlockData, ValidationParams}, primitives::{BlockData, ValidationParams},
wasm_executor::EXECUTION_TIMEOUT_SEC, wasm_executor::{ValidationError, InvalidCandidate, EXECUTION_TIMEOUT_SEC},
}; };
#[test] #[test]
@@ -40,7 +40,7 @@ fn terminates_on_timeout() {
sp_core::testing::TaskExecutor::new(), sp_core::testing::TaskExecutor::new(),
); );
match result { match result {
Err(parachain::wasm_executor::Error::Timeout) => {}, Err(ValidationError::InvalidCandidate(InvalidCandidate::Timeout)) => {},
r => panic!("{:?}", r), r => panic!("{:?}", r),
} }
+1 -1
View File
@@ -26,7 +26,7 @@ pub enum Error {
/// Consensus error /// Consensus error
Consensus(consensus::error::Error), Consensus(consensus::error::Error),
/// A wasm-validation error. /// A wasm-validation error.
WasmValidation(parachain::wasm_executor::Error), WasmValidation(parachain::wasm_executor::ValidationError),
/// An I/O error. /// An I/O error.
Io(std::io::Error), Io(std::io::Error),
/// An error in the availability erasure-coding. /// An error in the availability erasure-coding.