Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Expose validation specific host function. #2366

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 17 additions & 14 deletions node/core/candidate-validation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ async fn runtime_api_request<T>(

#[derive(Debug)]
enum AssumptionCheckOutcome {
Matches(PersistedValidationData, ValidationCode),
Matches(PersistedValidationData, Arc<ValidationCode>),
DoesNotMatch,
BadRequest,
}
Expand Down Expand Up @@ -227,7 +227,7 @@ async fn check_assumption_validation_data(

match validation_code {
Ok(None) | Err(_) => AssumptionCheckOutcome::BadRequest,
Ok(Some(v)) => AssumptionCheckOutcome::Matches(validation_data, v),
Ok(Some(v)) => AssumptionCheckOutcome::Matches(validation_data, Arc::new(v)),
}
} else {
AssumptionCheckOutcome::DoesNotMatch
Expand Down Expand Up @@ -333,7 +333,7 @@ async fn spawn_validate_exhaustive(
ctx: &mut impl SubsystemContext<Message = CandidateValidationMessage>,
isolation_strategy: IsolationStrategy,
persisted_validation_data: PersistedValidationData,
validation_code: ValidationCode,
validation_code: Arc<ValidationCode>,
descriptor: CandidateDescriptor,
pov: Arc<PoV>,
spawn: impl SpawnNamed + 'static,
Expand Down Expand Up @@ -390,7 +390,7 @@ trait ValidationBackend {

fn validate<S: SpawnNamed + 'static>(
arg: Self::Arg,
validation_code: &ValidationCode,
validation_code: Arc<ValidationCode>,
params: ValidationParams,
spawn: S,
) -> Result<WasmValidationResult, ValidationError>;
Expand All @@ -403,12 +403,14 @@ impl ValidationBackend for RealValidationBackend {

fn validate<S: SpawnNamed + 'static>(
isolation_strategy: IsolationStrategy,
validation_code: &ValidationCode,
validation_code: Arc<ValidationCode>,
params: ValidationParams,
spawn: S,
) -> Result<WasmValidationResult, ValidationError> {
let ext = validation_code.clone();
wasm_executor::validate_candidate(
&validation_code.0,
validation_code.0.as_slice(),
ext,
params,
&isolation_strategy,
spawn,
Expand All @@ -423,7 +425,7 @@ impl ValidationBackend for RealValidationBackend {
fn validate_candidate_exhaustive<B: ValidationBackend, S: SpawnNamed + 'static>(
backend_arg: B::Arg,
persisted_validation_data: PersistedValidationData,
validation_code: ValidationCode,
validation_code: Arc<ValidationCode>,
descriptor: CandidateDescriptor,
pov: Arc<PoV>,
spawn: S,
Expand All @@ -442,7 +444,7 @@ fn validate_candidate_exhaustive<B: ValidationBackend, S: SpawnNamed + 'static>(
relay_parent_storage_root: persisted_validation_data.relay_parent_storage_root,
};

match B::validate(backend_arg, &validation_code, params, spawn) {
match B::validate(backend_arg, validation_code, params, spawn) {
Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::Timeout)) =>
Ok(ValidationResult::Invalid(InvalidCandidate::Timeout)),
Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::ParamsTooLarge(l))) =>
Expand Down Expand Up @@ -573,6 +575,7 @@ mod tests {
use futures::executor;
use assert_matches::assert_matches;
use sp_keyring::Sr25519Keyring;
use std::sync::Arc;

struct MockValidationBackend;

Expand All @@ -585,7 +588,7 @@ mod tests {

fn validate<S: SpawnNamed + 'static>(
arg: Self::Arg,
_validation_code: &ValidationCode,
_validation_code: Arc<ValidationCode>,
_params: ValidationParams,
_spawn: S,
) -> Result<WasmValidationResult, ValidationError> {
Expand Down Expand Up @@ -662,7 +665,7 @@ mod tests {

assert_matches!(check_result.await.unwrap(), AssumptionCheckOutcome::Matches(o, v) => {
assert_eq!(o, validation_data);
assert_eq!(v, validation_code);
assert_eq!(v.as_ref(), &validation_code);
});
};

Expand Down Expand Up @@ -726,7 +729,7 @@ mod tests {

assert_matches!(check_result.await.unwrap(), AssumptionCheckOutcome::Matches(o, v) => {
assert_eq!(o, validation_data);
assert_eq!(v, validation_code);
assert_eq!(v.as_ref(), &validation_code);
});
};

Expand Down Expand Up @@ -910,7 +913,7 @@ mod tests {
let v = validate_candidate_exhaustive::<MockValidationBackend, _>(
MockValidationArg { result: Ok(validation_result) },
validation_data.clone(),
vec![1, 2, 3].into(),
Arc::new(vec![1, 2, 3].into()),
descriptor,
Arc::new(pov),
TaskExecutor::new(),
Expand Down Expand Up @@ -946,7 +949,7 @@ mod tests {
))
},
validation_data,
vec![1, 2, 3].into(),
Arc::new(vec![1, 2, 3].into()),
descriptor,
Arc::new(pov),
TaskExecutor::new(),
Expand Down Expand Up @@ -975,7 +978,7 @@ mod tests {
))
},
validation_data,
vec![1, 2, 3].into(),
Arc::new(vec![1, 2, 3].into()),
descriptor,
Arc::new(pov),
TaskExecutor::new(),
Expand Down
20 changes: 16 additions & 4 deletions node/service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,28 +78,40 @@ native_executor_instance!(
pub PolkadotExecutor,
polkadot_runtime::api::dispatch,
polkadot_runtime::native_version,
frame_benchmarking::benchmarking::HostFunctions,
(
frame_benchmarking::benchmarking::HostFunctions,
polkadot_parachain::validation::HostFunctions,
),
);

native_executor_instance!(
pub KusamaExecutor,
kusama_runtime::api::dispatch,
kusama_runtime::native_version,
frame_benchmarking::benchmarking::HostFunctions,
(
frame_benchmarking::benchmarking::HostFunctions,
polkadot_parachain::validation::HostFunctions,
),
);

native_executor_instance!(
pub WestendExecutor,
westend_runtime::api::dispatch,
westend_runtime::native_version,
frame_benchmarking::benchmarking::HostFunctions,
(
frame_benchmarking::benchmarking::HostFunctions,
polkadot_parachain::validation::HostFunctions,
),
);

native_executor_instance!(
pub RococoExecutor,
rococo_runtime::api::dispatch,
rococo_runtime::native_version,
frame_benchmarking::benchmarking::HostFunctions,
(
frame_benchmarking::benchmarking::HostFunctions,
polkadot_parachain::validation::HostFunctions,
),
);

#[derive(thiserror::Error, Debug)]
Expand Down
2 changes: 1 addition & 1 deletion node/subsystem/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ pub enum CandidateValidationMessage {
/// performed by the relay-chain.
ValidateFromExhaustive(
PersistedValidationData,
ValidationCode,
Arc<ValidationCode>,
CandidateDescriptor,
Arc<PoV>,
oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
Expand Down
5 changes: 4 additions & 1 deletion node/test/service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ native_executor_instance!(
pub PolkadotTestExecutor,
polkadot_test_runtime::api::dispatch,
polkadot_test_runtime::native_version,
frame_benchmarking::benchmarking::HostFunctions,
(
frame_benchmarking::benchmarking::HostFunctions,
polkadot_parachain::validation::HostFunctions,
),
);

/// The client type being used by the test service.
Expand Down
5 changes: 3 additions & 2 deletions parachain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-wasm-interface = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-runtime-interface = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
polkadot-core-primitives = { path = "../core-primitives", default-features = false }
derive_more = "0.99.11"
sp-externalities = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }

# all optional crates.
thiserror = { version = "1.0.22", optional = true }
serde = { version = "1.0.117", default-features = false, features = [ "derive" ], optional = true }
sp-externalities = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true }
sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true }
parking_lot = { version = "0.11.1", optional = true }
Expand All @@ -46,7 +47,7 @@ std = [
"parking_lot",
"log",
"parity-util-mem",
"sp-externalities",
"sp-externalities/std",
"sc-executor",
"sp-io",
"polkadot-core-primitives/std",
Expand Down
52 changes: 52 additions & 0 deletions parachain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,55 @@ mod wasm_api;

#[cfg(all(not(feature = "std"), feature = "wasm-api"))]
pub use wasm_api::*;

use sp_std::{vec::Vec, boxed::Box};
use crate::primitives::ValidationCode;

use sp_runtime_interface::runtime_interface;

/// Validation specific host functions.
#[runtime_interface]
pub trait Validation {
/// Get validation wasm bytecode.
fn validation_code(&mut self) -> Vec<u8> {
use sp_externalities::ExternalitiesExt;
let extension = self.extension::<ValidationExt>()
.expect("Cannot get validation code without dynamic runtime dispatcher (ValidationExt)");
extension.validation_code()
}
}

#[cfg(feature = "std")]
sp_externalities::decl_extension! {
/// executor extension.
pub struct ValidationExt(Box<dyn Validation>);
}

#[cfg(feature = "std")]
impl ValidationExt {
/// New instance of task executor extension.
pub fn new(validation_ext: impl Validation) -> Self {
Self(Box::new(validation_ext))
}
}

/// Base methods to implement validation extension.
pub trait Validation: Send + 'static {
/// Get the validation code currently running.
/// This can be use to check validity or to complete
/// proofs.
fn validation_code(&self) -> Vec<u8>;
}

#[cfg(feature = "std")]
impl Validation for std::sync::Arc<ValidationCode> {
fn validation_code(&self) -> Vec<u8> {
self.0.clone()
}
}

impl<'a> Validation for &'static [u8] {
fn validation_code(&self) -> Vec<u8> {
self.to_vec()
}
}
9 changes: 8 additions & 1 deletion parachain/src/wasm_executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use parity_scale_codec::{Decode, Encode};
use sp_core::{storage::{ChildInfo, TrackedStorageKey}, traits::{CallInWasm, SpawnNamed}};
use sp_externalities::Extensions;
use sp_wasm_interface::HostFunctions as _;
use crate::{Validation, ValidationExt};

#[cfg(not(any(target_os = "android", target_os = "unknown")))]
pub use validation_host::{run_worker, ValidationPool, EXECUTION_TIMEOUT_SEC, WORKER_ARGS};
Expand Down Expand Up @@ -167,14 +168,16 @@ pub struct ExecutorCache(sc_executor::WasmExecutor);

impl Default for ExecutorCache {
fn default() -> Self {
let mut host_functions = HostFunctions::host_functions();
host_functions.extend(crate::validation::HostFunctions::host_functions().into_iter());
ExecutorCache(sc_executor::WasmExecutor::new(
#[cfg(all(feature = "wasmtime", not(any(target_os = "android", target_os = "unknown"))))]
sc_executor::WasmExecutionMethod::Compiled,
#[cfg(any(not(feature = "wasmtime"), target_os = "android", target_os = "unknown"))]
sc_executor::WasmExecutionMethod::Interpreted,
// TODO: Make sure we don't use more than 1GB: https://github.com/paritytech/polkadot/issues/699
Some(1024),
HostFunctions::host_functions(),
host_functions,
8
))
}
Expand All @@ -185,6 +188,7 @@ impl Default for ExecutorCache {
/// This will fail if the validation code is not a proper parachain validation module.
pub fn validate_candidate(
validation_code: &[u8],
validation_ext: impl Validation,
params: ValidationParams,
isolation_strategy: &IsolationStrategy,
spawner: impl SpawnNamed + 'static,
Expand All @@ -196,6 +200,7 @@ pub fn validate_candidate(
validation_code,
&params.encode(),
spawner,
validation_ext,
)
},
#[cfg(not(any(target_os = "android", target_os = "unknown")))]
Expand All @@ -221,10 +226,12 @@ pub fn validate_candidate_internal(
validation_code: &[u8],
encoded_call_data: &[u8],
spawner: impl SpawnNamed + 'static,
validation_ext: impl Validation,
) -> Result<ValidationResult, ValidationError> {
let executor = &executor.0;

let mut extensions = Extensions::new();
extensions.register(ValidationExt::new(validation_ext));
extensions.register(sp_core::traits::TaskExecutorExt::new(spawner));
extensions.register(sp_core::traits::CallInWasmExt::new(executor.clone()));

Expand Down
14 changes: 11 additions & 3 deletions parachain/src/wasm_executor/validation_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

use std::{process, env, sync::Arc, sync::atomic, path::PathBuf};
use parity_scale_codec::{Decode, Encode};
use crate::primitives::{ValidationParams, ValidationResult};
use crate::primitives::{ValidationParams, ValidationResult, ValidationCode};
use super::{
validate_candidate_internal, ValidationError, InvalidCandidate, InternalError,
MAX_CODE_MEM, MAX_RUNTIME_MEM, MAX_VALIDATION_RESULT_HEADER_MEM,
Expand Down Expand Up @@ -185,8 +185,16 @@ pub fn run_worker(mem_id: &str) -> Result<(), String> {
let (code, _) = code.split_at_mut(header.code_size as usize);
let (call_data, _) = rest.split_at_mut(MAX_RUNTIME_MEM);
let (call_data, _) = call_data.split_at_mut(header.params_size as usize);

let result = validate_candidate_internal(&executor, code, call_data, task_executor.clone());
// Costy code clone, could also use an unsafe implementation of ValidationExt.
let validation_ext = Arc::new(ValidationCode(code.to_vec()));

let result = validate_candidate_internal(
&executor,
code,
call_data,
task_executor.clone(),
validation_ext,
);
debug!(target: LOG_TARGET, "{} Candidate validated: {:?}", process::id(), result);

match result {
Expand Down
Loading