mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-24 07:45:49 +00:00
Parachain validation moved to external process (#325)
* Improved execution & tests * Style * Made CLI arg const * Moved Upwards message * CLI subcommand for validation worker * Build halting parachain * Build halting parachain * Made stuff private * Reorganized parachain tests * Comment * Whitespace * Apply suggestions from code review Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Fixed call data size check and introduced an enum * Apply suggestions from code review Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
committed by
Bastian Köcher
parent
157cd9a217
commit
f1fdb0cb83
Generated
+466
-352
File diff suppressed because it is too large
Load Diff
@@ -10,5 +10,6 @@ log = "0.4.6"
|
|||||||
tokio = "0.1.7"
|
tokio = "0.1.7"
|
||||||
futures = "0.1.17"
|
futures = "0.1.17"
|
||||||
exit-future = "0.1"
|
exit-future = "0.1"
|
||||||
|
structopt = "0.2"
|
||||||
cli = { package = "substrate-cli", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
|
cli = { package = "substrate-cli", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
|
||||||
service = { package = "polkadot-service", path = "../service" }
|
service = { package = "polkadot-service", path = "../service" }
|
||||||
|
|||||||
+29
-14
@@ -28,6 +28,7 @@ use tokio::runtime::Runtime;
|
|||||||
use service::Service as BareService;
|
use service::Service as BareService;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
pub use service::{
|
pub use service::{
|
||||||
Components as ServiceComponents, PolkadotService, CustomConfiguration, ServiceFactory, Factory,
|
Components as ServiceComponents, PolkadotService, CustomConfiguration, ServiceFactory, Factory,
|
||||||
@@ -65,21 +66,28 @@ pub trait Worker: IntoExit {
|
|||||||
fn work<S: PolkadotService>(self, service: &S, executor: TaskExecutor) -> Self::Work;
|
fn work<S: PolkadotService>(self, service: &S, executor: TaskExecutor) -> Self::Work;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse command line arguments into service configuration.
|
#[derive(Debug, StructOpt, Clone)]
|
||||||
///
|
enum PolkadotSubCommands {
|
||||||
/// IANA unassigned port ranges that we could use:
|
#[structopt(name = "validation-worker", raw(setting = "structopt::clap::AppSettings::Hidden"))]
|
||||||
/// 6717-6766 Unassigned
|
ValidationWorker(ValidationWokerCommand),
|
||||||
/// 8504-8553 Unassigned
|
}
|
||||||
/// 9556-9591 Unassigned
|
|
||||||
/// 9803-9874 Unassigned
|
impl cli::GetLogFilter for PolkadotSubCommands {
|
||||||
/// 9926-9949 Unassigned
|
fn get_log_filter(&self) -> Option<String> { None }
|
||||||
pub fn run<I, T, W>(args: I, worker: W, version: cli::VersionInfo) -> error::Result<()> where
|
}
|
||||||
I: IntoIterator<Item = T>,
|
|
||||||
T: Into<std::ffi::OsString> + Clone,
|
#[derive(Debug, StructOpt, Clone)]
|
||||||
|
struct ValidationWokerCommand {
|
||||||
|
#[structopt()]
|
||||||
|
pub mem_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses polkadot specific CLI arguments and run the service.
|
||||||
|
pub fn run<W>(worker: W, version: cli::VersionInfo) -> error::Result<()> where
|
||||||
W: Worker,
|
W: Worker,
|
||||||
{
|
{
|
||||||
cli::parse_and_execute::<service::Factory, NoCustom, NoCustom, _, _, _, _, _>(
|
let command = cli::parse_and_execute::<service::Factory, PolkadotSubCommands, NoCustom, _, _, _, _, _>(
|
||||||
load_spec, &version, "parity-polkadot", args, worker,
|
load_spec, &version, "parity-polkadot", std::env::args(), worker,
|
||||||
|worker, _cli_args, _custom_args, mut config| {
|
|worker, _cli_args, _custom_args, mut config| {
|
||||||
info!("{}", version.name);
|
info!("{}", version.name);
|
||||||
info!(" version {}", config.full_version());
|
info!(" version {}", config.full_version());
|
||||||
@@ -103,7 +111,14 @@ pub fn run<I, T, W>(args: I, worker: W, version: cli::VersionInfo) -> error::Res
|
|||||||
),
|
),
|
||||||
}.map_err(|e| format!("{:?}", e))
|
}.map_err(|e| format!("{:?}", e))
|
||||||
}
|
}
|
||||||
).map_err(Into::into).map(|_| ())
|
)?;
|
||||||
|
|
||||||
|
match command {
|
||||||
|
Some(PolkadotSubCommands::ValidationWorker(args)) => {
|
||||||
|
service::run_validation_worker(&args.mem_id).map_err(Into::into)
|
||||||
|
}
|
||||||
|
_ => Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_until_exit<T, C, W>(
|
fn run_until_exit<T, C, W>(
|
||||||
|
|||||||
@@ -429,12 +429,11 @@ fn compute_targets(para_id: ParaId, session_keys: &[SessionKey], roster: DutyRos
|
|||||||
///
|
///
|
||||||
/// Provide a future which resolves when the node should exit.
|
/// Provide a future which resolves when the node should exit.
|
||||||
/// This function blocks until done.
|
/// This function blocks until done.
|
||||||
pub fn run_collator<P, E, I, ArgT>(
|
pub fn run_collator<P, E>(
|
||||||
build_parachain_context: P,
|
build_parachain_context: P,
|
||||||
para_id: ParaId,
|
para_id: ParaId,
|
||||||
exit: E,
|
exit: E,
|
||||||
key: Arc<ed25519::Pair>,
|
key: Arc<ed25519::Pair>,
|
||||||
args: I,
|
|
||||||
version: VersionInfo,
|
version: VersionInfo,
|
||||||
) -> polkadot_cli::error::Result<()> where
|
) -> polkadot_cli::error::Result<()> where
|
||||||
P: BuildParachainContext + Send + 'static,
|
P: BuildParachainContext + Send + 'static,
|
||||||
@@ -442,11 +441,9 @@ pub fn run_collator<P, E, I, ArgT>(
|
|||||||
<<P::ParachainContext as ParachainContext>::ProduceCandidate as IntoFuture>::Future: Send + 'static,
|
<<P::ParachainContext as ParachainContext>::ProduceCandidate as IntoFuture>::Future: Send + 'static,
|
||||||
E: IntoFuture<Item=(), Error=()>,
|
E: IntoFuture<Item=(), Error=()>,
|
||||||
E::Future: Send + Clone + Sync + 'static,
|
E::Future: Send + Clone + Sync + 'static,
|
||||||
I: IntoIterator<Item=ArgT>,
|
|
||||||
ArgT: Into<std::ffi::OsString> + Clone,
|
|
||||||
{
|
{
|
||||||
let node_logic = CollationNode { build_parachain_context, exit: exit.into_future(), para_id, key };
|
let node_logic = CollationNode { build_parachain_context, exit: exit.into_future(), para_id, key };
|
||||||
polkadot_cli::run(args, node_logic, version)
|
polkadot_cli::run(node_logic, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -12,12 +12,17 @@ derive_more = { version = "0.14", optional = true }
|
|||||||
serde = { version = "1.0", default-features = false, features = [ "derive" ] }
|
serde = { version = "1.0", default-features = false, features = [ "derive" ] }
|
||||||
|
|
||||||
rstd = { package = "sr-std", git = "https://github.com/paritytech/substrate", branch = "polkadot-master", default-features = false }
|
rstd = { package = "sr-std", git = "https://github.com/paritytech/substrate", branch = "polkadot-master", default-features = false }
|
||||||
|
shared_memory = { version = "0.8", optional = true }
|
||||||
|
lazy_static = { version = "1.3.0", optional = true }
|
||||||
|
parking_lot = { version = "0.7.1", optional = true }
|
||||||
|
log = { version = "0.4.6", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tiny-keccak = "1.4"
|
tiny-keccak = "1.4"
|
||||||
adder = { path = "../test-parachains/adder" }
|
adder = { path = "../test-parachains/adder" }
|
||||||
|
halt = { path = "../test-parachains/halt" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
wasm-api = []
|
wasm-api = []
|
||||||
std = [ "codec/std", "wasmi", "derive_more", "serde/std", "rstd/std" ]
|
std = [ "codec/std", "wasmi", "derive_more", "serde/std", "rstd/std", "shared_memory", "lazy_static", "parking_lot", "log" ]
|
||||||
|
|||||||
@@ -207,3 +207,13 @@ pub struct UpwardMessageRef<'a> {
|
|||||||
/// Underlying data of the message.
|
/// Underlying data of the message.
|
||||||
pub data: &'a [u8],
|
pub data: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A message from a parachain to its Relay Chain.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Debug))]
|
||||||
|
pub struct UpwardMessage {
|
||||||
|
/// The origin for the message to be sent from.
|
||||||
|
pub origin: ParachainDispatchOrigin,
|
||||||
|
/// The message data.
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,14 +20,39 @@
|
|||||||
//! Assuming the parameters are correct, this module provides a wrapper around
|
//! Assuming the parameters are correct, this module provides a wrapper around
|
||||||
//! a WASM VM for re-execution of a parachain candidate.
|
//! a WASM VM for re-execution of a parachain candidate.
|
||||||
|
|
||||||
use std::{cell::RefCell, fmt, convert::TryInto};
|
use std::{cell::RefCell, fmt, convert::TryInto, process, env};
|
||||||
|
use std::sync::{Arc, atomic};
|
||||||
use crate::codec::{Decode, Encode};
|
use crate::codec::{Decode, Encode};
|
||||||
use wasmi::{
|
use wasmi::{
|
||||||
self, Module, ModuleInstance, Trap, MemoryInstance, MemoryDescriptor, MemoryRef,
|
self, Module, ModuleInstance, Trap, MemoryInstance, MemoryDescriptor, MemoryRef,
|
||||||
ModuleImportResolver, RuntimeValue, Externals, Error as WasmError, ValueType,
|
ModuleImportResolver, RuntimeValue, Externals, Error as WasmError, ValueType,
|
||||||
memory_units::{self, Bytes, Pages, RoundUpTo}
|
memory_units::{self, Bytes, Pages, RoundUpTo}
|
||||||
};
|
};
|
||||||
use super::{ValidationParams, ValidationResult, MessageRef, UpwardMessageRef};
|
use super::{ValidationParams, ValidationResult, MessageRef, UpwardMessageRef, UpwardMessage, IncomingMessage};
|
||||||
|
use shared_memory::{SharedMem, SharedMemConf, EventState, WriteLockable, EventWait, EventSet};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use log::{trace, debug};
|
||||||
|
|
||||||
|
// maximum memory in bytes
|
||||||
|
const MAX_RUNTIME_MEM: usize = 1024 * 1024 * 1024; // 1 GiB
|
||||||
|
const MAX_CODE_MEM: usize = 16 * 1024 * 1024; // 16 MiB
|
||||||
|
// Message data limit
|
||||||
|
const MAX_MESSAGE_MEM: usize = 16 * 1024 * 1024; // 16 MiB
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
enum Event {
|
||||||
|
CandidateReady = 0,
|
||||||
|
ResultReady = 1,
|
||||||
|
WorkerReady = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref HOST: Mutex<ValidationHost> = Mutex::new(ValidationHost::new());
|
||||||
|
}
|
||||||
|
|
||||||
mod ids {
|
mod ids {
|
||||||
/// Post a message to another parachain.
|
/// Post a message to another parachain.
|
||||||
@@ -37,6 +62,16 @@ mod ids {
|
|||||||
pub const POST_UPWARDS_MESSAGE: usize = 2;
|
pub const POST_UPWARDS_MESSAGE: usize = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// WASM code execution mode.
|
||||||
|
pub enum ExecutionMode {
|
||||||
|
/// Execute in-process. The execution can not be interrupted or aborted.
|
||||||
|
Local,
|
||||||
|
/// Remote execution in a spawned process.
|
||||||
|
Remote,
|
||||||
|
/// Remote execution in a spawned test runner.
|
||||||
|
RemoteTest,
|
||||||
|
}
|
||||||
|
|
||||||
/// Error type for the wasm executor
|
/// 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 {
|
pub enum Error {
|
||||||
@@ -44,12 +79,23 @@ pub enum Error {
|
|||||||
Wasm(WasmError),
|
Wasm(WasmError),
|
||||||
/// Externalities error
|
/// Externalities error
|
||||||
Externalities(ExternalitiesError),
|
Externalities(ExternalitiesError),
|
||||||
/// Call data too big. WASM32 only has a 32-bit address space.
|
/// Code size it too large.
|
||||||
#[display(fmt = "Validation parameters took up {} bytes, max allowed by WASM is {}", _0, i32::max_value())]
|
#[display(fmt = "WASM code is {} bytes, max allowed is {}", _0, MAX_CODE_MEM)]
|
||||||
|
CodeTooLarge(usize),
|
||||||
|
/// Call data is too large.
|
||||||
|
#[display(fmt = "Validation parameters are {} bytes, max allowed is {}", _0, MAX_RUNTIME_MEM)]
|
||||||
ParamsTooLarge(usize),
|
ParamsTooLarge(usize),
|
||||||
/// Bad return data or type.
|
/// Bad return data or type.
|
||||||
#[display(fmt = "Validation function returned invalid data.")]
|
#[display(fmt = "Validation function returned invalid data.")]
|
||||||
BadReturn,
|
BadReturn,
|
||||||
|
#[display(fmt = "Validation function timeout.")]
|
||||||
|
Timeout,
|
||||||
|
#[display(fmt = "IO error: {}", _0)]
|
||||||
|
Io(std::io::Error),
|
||||||
|
#[display(fmt = "System error: {}", _0)]
|
||||||
|
System(Box<dyn std::error::Error>),
|
||||||
|
#[display(fmt = "WASM worker error: {}", _0)]
|
||||||
|
External(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for Error {
|
impl std::error::Error for Error {
|
||||||
@@ -57,6 +103,8 @@ impl std::error::Error for Error {
|
|||||||
match self {
|
match self {
|
||||||
Error::Wasm(ref err) => Some(err),
|
Error::Wasm(ref err) => Some(err),
|
||||||
Error::Externalities(ref err) => Some(err),
|
Error::Externalities(ref err) => Some(err),
|
||||||
|
Error::Io(ref err) => Some(err),
|
||||||
|
Error::System(ref err) => Some(&**err),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,6 +286,296 @@ impl<'a, E: 'a + Externalities> Externals for ValidationExternals<'a, E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Params header in shared memory. All offsets should be aligned to WASM page size.
|
||||||
|
#[derive(Encode, Decode, Debug)]
|
||||||
|
struct ValidationHeader {
|
||||||
|
code_size: u64,
|
||||||
|
params_size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Debug)]
|
||||||
|
pub enum ValidationResultHeader {
|
||||||
|
Ok {
|
||||||
|
result: ValidationResult,
|
||||||
|
egress_message_count: u64,
|
||||||
|
up_message_count: u64,
|
||||||
|
},
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct WorkerExternalities {
|
||||||
|
egress_data: Vec<u8>,
|
||||||
|
egress_message_count: usize,
|
||||||
|
up_data: Vec<u8>,
|
||||||
|
up_message_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Externalities for WorkerExternalities {
|
||||||
|
fn post_message(&mut self, message: MessageRef) -> Result<(), ExternalitiesError> {
|
||||||
|
IncomingMessage {
|
||||||
|
source: message.target,
|
||||||
|
data: message.data.to_vec(),
|
||||||
|
}
|
||||||
|
.encode_to(&mut self.egress_data);
|
||||||
|
self.egress_message_count += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_upward_message(&mut self, message: UpwardMessageRef) -> Result<(), ExternalitiesError> {
|
||||||
|
UpwardMessage {
|
||||||
|
origin: message.origin,
|
||||||
|
data: message.data.to_vec(),
|
||||||
|
}
|
||||||
|
.encode_to(&mut self.up_data);
|
||||||
|
self.up_message_count += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validation worker process entry point. Runs a loop waiting for canidates to validate
|
||||||
|
/// and sends back results via shared memory.
|
||||||
|
pub fn run_worker(mem_id: &str) -> Result<(), String> {
|
||||||
|
let mut memory = match SharedMem::open(mem_id) {
|
||||||
|
Ok(memory) => memory,
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Error opening shared memory: {:?}", e);
|
||||||
|
return Err(format!("Error opening shared memory: {:?}", e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut externalities = WorkerExternalities::default();
|
||||||
|
|
||||||
|
let exit = Arc::new(atomic::AtomicBool::new(false));
|
||||||
|
// spawn parent monitor thread
|
||||||
|
let watch_exit = exit.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
use std::io::Read;
|
||||||
|
let mut in_data = Vec::new();
|
||||||
|
std::io::stdin().read_to_end(&mut in_data).ok(); // pipe terminates when parent process exits
|
||||||
|
debug!("Parent process is dead. Exiting");
|
||||||
|
exit.store(true, atomic::Ordering::Relaxed);
|
||||||
|
});
|
||||||
|
|
||||||
|
memory.set(Event::WorkerReady as usize, EventState::Signaled)
|
||||||
|
.map_err(|e| format!("Error setting shared event: {:?}", e))?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if watch_exit.load(atomic::Ordering::Relaxed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Waiting for candidate");
|
||||||
|
match memory.wait(Event::CandidateReady as usize, shared_memory::Timeout::Sec(1)) {
|
||||||
|
Err(e) => {
|
||||||
|
// Timeout
|
||||||
|
trace!("Timeout waiting for candidate: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Ok(()) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
debug!("Processing candidate");
|
||||||
|
// we have candidate data
|
||||||
|
let mut slice = memory.wlock_as_slice(0)
|
||||||
|
.map_err(|e| format!("Error locking shared memory: {:?}", e))?;
|
||||||
|
|
||||||
|
let result = {
|
||||||
|
let data: &mut[u8] = &mut **slice;
|
||||||
|
let (header_buf, rest) = data.split_at_mut(1024);
|
||||||
|
let mut header_buf: &[u8] = header_buf;
|
||||||
|
let header = ValidationHeader::decode(&mut header_buf)
|
||||||
|
.ok_or_else(|| format!("Error decoding validation request."))?;
|
||||||
|
debug!("Candidate header: {:?}", header);
|
||||||
|
let (code, rest) = rest.split_at_mut(MAX_CODE_MEM);
|
||||||
|
let (code, _) = code.split_at_mut(header.code_size as usize);
|
||||||
|
let (call_data, rest) = rest.split_at_mut(MAX_RUNTIME_MEM);
|
||||||
|
let (call_data, _) = call_data.split_at_mut(header.params_size as usize);
|
||||||
|
let message_data = rest;
|
||||||
|
|
||||||
|
let result = validate_candidate_internal(code, call_data, &mut externalities);
|
||||||
|
debug!("Candidate validated: {:?}", result);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(r) => {
|
||||||
|
if externalities.egress_data.len() + externalities.up_data.len() > MAX_MESSAGE_MEM {
|
||||||
|
ValidationResultHeader::Error("Message data is too large".into())
|
||||||
|
} else {
|
||||||
|
let e_len = externalities.egress_data.len();
|
||||||
|
let up_len = externalities.up_data.len();
|
||||||
|
message_data[0..e_len].copy_from_slice(&externalities.egress_data);
|
||||||
|
message_data[e_len..(e_len + up_len)].copy_from_slice(&externalities.up_data);
|
||||||
|
ValidationResultHeader::Ok {
|
||||||
|
result: r,
|
||||||
|
egress_message_count: externalities.egress_message_count as u64,
|
||||||
|
up_message_count: externalities.up_message_count as u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => ValidationResultHeader::Error(e.to_string()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut data: &mut[u8] = &mut **slice;
|
||||||
|
result.encode_to(&mut data);
|
||||||
|
}
|
||||||
|
debug!("Signaling result");
|
||||||
|
memory.set(Event::ResultReady as usize, EventState::Signaled)
|
||||||
|
.map_err(|e| format!("Error setting shared event: {:?}", e))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for ValidationHost {}
|
||||||
|
|
||||||
|
struct ValidationHost {
|
||||||
|
worker: Option<process::Child>,
|
||||||
|
memory: Option<SharedMem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Drop for ValidationHost {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(ref mut worker) = &mut self.worker {
|
||||||
|
worker.kill().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValidationHost {
|
||||||
|
fn create_memory() -> Result<SharedMem, Error> {
|
||||||
|
let mem_size = MAX_RUNTIME_MEM + MAX_CODE_MEM + MAX_MESSAGE_MEM + 1024;
|
||||||
|
let mem_config = SharedMemConf::new()
|
||||||
|
.set_size(mem_size)
|
||||||
|
.add_lock(shared_memory::LockType::Mutex, 0, mem_size)?
|
||||||
|
.add_event(shared_memory::EventType::Auto)? // Event::CandidateReady
|
||||||
|
.add_event(shared_memory::EventType::Auto)? // Event::ResultReady
|
||||||
|
.add_event(shared_memory::EventType::Auto)?; // Evebt::WorkerReady
|
||||||
|
|
||||||
|
Ok(mem_config.create()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new() -> ValidationHost {
|
||||||
|
ValidationHost {
|
||||||
|
worker: None,
|
||||||
|
memory: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_worker(&mut self, test_mode: bool) -> Result<(), Error> {
|
||||||
|
if let Some(ref mut worker) = self.worker {
|
||||||
|
// Check if still alive
|
||||||
|
if let Ok(None) = worker.try_wait() {
|
||||||
|
// Still running
|
||||||
|
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.worker = Some(worker);
|
||||||
|
|
||||||
|
memory.wait(Event::WorkerReady as usize, shared_memory::Timeout::Sec(5))?;
|
||||||
|
self.memory = Some(memory);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate a candidate under the given validation code.
|
||||||
|
///
|
||||||
|
/// This will fail if the validation code is not a proper parachain validation module.
|
||||||
|
fn validate_candidate<E: Externalities>(
|
||||||
|
&mut self,
|
||||||
|
validation_code: &[u8],
|
||||||
|
params: ValidationParams,
|
||||||
|
externalities: &mut E,
|
||||||
|
test_mode: bool,
|
||||||
|
) -> Result<ValidationResult, Error>
|
||||||
|
{
|
||||||
|
if validation_code.len() > MAX_CODE_MEM {
|
||||||
|
return Err(Error::CodeTooLarge(validation_code.len()));
|
||||||
|
}
|
||||||
|
// First, check if need to spawn the child process
|
||||||
|
self.start_worker(test_mode)?;
|
||||||
|
let memory = self.memory.as_mut().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 (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());
|
||||||
|
let (call_data, _) = rest.split_at_mut(MAX_RUNTIME_MEM);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
call_data[..encoded_params.len()].copy_from_slice(&encoded_params);
|
||||||
|
|
||||||
|
let header = ValidationHeader {
|
||||||
|
code_size: validation_code.len() as u64,
|
||||||
|
params_size: encoded_params.len() as u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
header.encode_to(&mut header_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Signaling candidate");
|
||||||
|
memory.set(Event::CandidateReady as usize, EventState::Signaled)?;
|
||||||
|
|
||||||
|
debug!("Waiting for results");
|
||||||
|
match memory.wait(Event::ResultReady as usize, shared_memory::Timeout::Sec(5)) {
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Worker timeout: {:?}", e);
|
||||||
|
if let Some(mut worker) = self.worker.take() {
|
||||||
|
worker.kill().ok();
|
||||||
|
}
|
||||||
|
return Err(Error::Timeout.into());
|
||||||
|
}
|
||||||
|
Ok(()) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let data: &[u8] = &**memory.wlock_as_slice(0)?;
|
||||||
|
let (header_buf, rest) = data.split_at(1024);
|
||||||
|
let (_, rest) = rest.split_at(MAX_CODE_MEM);
|
||||||
|
let (_, message_data) = rest.split_at(MAX_RUNTIME_MEM);
|
||||||
|
let mut header_buf: &[u8] = header_buf;
|
||||||
|
let mut message_data: &[u8] = message_data;
|
||||||
|
let header = ValidationResultHeader::decode(&mut header_buf).unwrap();
|
||||||
|
match header {
|
||||||
|
ValidationResultHeader::Ok { result, egress_message_count, up_message_count } => {
|
||||||
|
for _ in 0 .. egress_message_count {
|
||||||
|
let message = IncomingMessage::decode(&mut message_data).unwrap();
|
||||||
|
let message_ref = MessageRef {
|
||||||
|
target: message.source,
|
||||||
|
data: &message.data,
|
||||||
|
};
|
||||||
|
externalities.post_message(message_ref)?;
|
||||||
|
}
|
||||||
|
for _ in 0 .. up_message_count {
|
||||||
|
let message = UpwardMessage::decode(&mut message_data).unwrap();
|
||||||
|
let message_ref = UpwardMessageRef {
|
||||||
|
origin: message.origin,
|
||||||
|
data: &message.data,
|
||||||
|
};
|
||||||
|
externalities.post_upward_message(message_ref)?;
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
ValidationResultHeader::Error(message) => {
|
||||||
|
Err(Error::External(message).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Validate a candidate under the given validation code.
|
/// Validate a candidate under the given validation code.
|
||||||
///
|
///
|
||||||
/// This will fail if the validation code is not a proper parachain validation module.
|
/// This will fail if the validation code is not a proper parachain validation module.
|
||||||
@@ -245,11 +583,29 @@ pub fn validate_candidate<E: Externalities>(
|
|||||||
validation_code: &[u8],
|
validation_code: &[u8],
|
||||||
params: ValidationParams,
|
params: ValidationParams,
|
||||||
externalities: &mut E,
|
externalities: &mut E,
|
||||||
) -> Result<ValidationResult, Error> {
|
options: ExecutionMode,
|
||||||
use wasmi::LINEAR_MEMORY_PAGE_SIZE;
|
) -> Result<ValidationResult, Error>
|
||||||
|
{
|
||||||
|
match options {
|
||||||
|
ExecutionMode::Local => {
|
||||||
|
validate_candidate_internal(validation_code, ¶ms.encode(), externalities)
|
||||||
|
},
|
||||||
|
ExecutionMode::Remote =>
|
||||||
|
HOST.lock().validate_candidate(validation_code, params, externalities, false),
|
||||||
|
ExecutionMode::RemoteTest =>
|
||||||
|
HOST.lock().validate_candidate(validation_code, params, externalities, true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// maximum memory in bytes
|
/// Validate a candidate under the given validation code.
|
||||||
const MAX_MEM: u32 = 1024 * 1024 * 1024; // 1 GiB
|
///
|
||||||
|
/// This will fail if the validation code is not a proper parachain validation module.
|
||||||
|
pub fn validate_candidate_internal<E: Externalities>(
|
||||||
|
validation_code: &[u8],
|
||||||
|
encoded_call_data: &[u8],
|
||||||
|
externalities: &mut E,
|
||||||
|
) -> Result<ValidationResult, Error> {
|
||||||
|
use wasmi::LINEAR_MEMORY_PAGE_SIZE;
|
||||||
|
|
||||||
// instantiate the module.
|
// instantiate the module.
|
||||||
let memory;
|
let memory;
|
||||||
@@ -258,7 +614,7 @@ pub fn validate_candidate<E: Externalities>(
|
|||||||
let module = Module::from_buffer(validation_code)?;
|
let module = Module::from_buffer(validation_code)?;
|
||||||
|
|
||||||
let module_resolver = Resolver {
|
let module_resolver = Resolver {
|
||||||
max_memory: MAX_MEM / LINEAR_MEMORY_PAGE_SIZE.0 as u32,
|
max_memory: (MAX_RUNTIME_MEM / LINEAR_MEMORY_PAGE_SIZE.0) as u32,
|
||||||
memory: RefCell::new(None),
|
memory: RefCell::new(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -285,8 +641,6 @@ pub fn validate_candidate<E: Externalities>(
|
|||||||
// - `offset` has alignment at least of 8,
|
// - `offset` has alignment at least of 8,
|
||||||
// - `len` is not zero.
|
// - `len` is not zero.
|
||||||
let (offset, len) = {
|
let (offset, len) = {
|
||||||
let encoded_call_data = params.encode();
|
|
||||||
|
|
||||||
// hard limit from WASM.
|
// hard limit from WASM.
|
||||||
if encoded_call_data.len() > i32::max_value() as usize {
|
if encoded_call_data.len() > i32::max_value() as usize {
|
||||||
return Err(Error::ParamsTooLarge(encoded_call_data.len()));
|
return Err(Error::ParamsTooLarge(encoded_call_data.len()));
|
||||||
|
|||||||
@@ -18,10 +18,8 @@
|
|||||||
|
|
||||||
use polkadot_parachain as parachain;
|
use polkadot_parachain as parachain;
|
||||||
|
|
||||||
use crate::parachain::{
|
use crate::parachain::{IncomingMessage, ValidationParams};
|
||||||
MessageRef, UpwardMessageRef, IncomingMessage, ValidationParams,
|
use crate::DummyExt;
|
||||||
wasm_executor::{Externalities, ExternalitiesError},
|
|
||||||
};
|
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
|
|
||||||
/// Head data for this parachain.
|
/// Head data for this parachain.
|
||||||
@@ -50,16 +48,6 @@ struct AddMessage {
|
|||||||
amount: u64,
|
amount: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DummyExt;
|
|
||||||
impl Externalities for DummyExt {
|
|
||||||
fn post_message(&mut self, _message: MessageRef) -> Result<(), ExternalitiesError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn post_upward_message(&mut self, _message: UpwardMessageRef) -> Result<(), ExternalitiesError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const TEST_CODE: &[u8] = adder::WASM_BINARY;
|
const TEST_CODE: &[u8] = adder::WASM_BINARY;
|
||||||
|
|
||||||
fn hash_state(state: u64) -> [u8; 32] {
|
fn hash_state(state: u64) -> [u8; 32] {
|
||||||
@@ -71,7 +59,7 @@ fn hash_head(head: &HeadData) -> [u8; 32] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_good_on_parent() {
|
pub fn execute_good_on_parent() {
|
||||||
let parent_head = HeadData {
|
let parent_head = HeadData {
|
||||||
number: 0,
|
number: 0,
|
||||||
parent_hash: [0; 32],
|
parent_hash: [0; 32],
|
||||||
@@ -91,6 +79,7 @@ fn execute_good_on_parent() {
|
|||||||
ingress: Vec::new(),
|
ingress: Vec::new(),
|
||||||
},
|
},
|
||||||
&mut DummyExt,
|
&mut DummyExt,
|
||||||
|
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let new_head = HeadData::decode(&mut &ret.head_data[..]).unwrap();
|
let new_head = HeadData::decode(&mut &ret.head_data[..]).unwrap();
|
||||||
@@ -126,6 +115,7 @@ fn execute_good_chain_on_parent() {
|
|||||||
ingress: Vec::new(),
|
ingress: Vec::new(),
|
||||||
},
|
},
|
||||||
&mut DummyExt,
|
&mut DummyExt,
|
||||||
|
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let new_head = HeadData::decode(&mut &ret.head_data[..]).unwrap();
|
let new_head = HeadData::decode(&mut &ret.head_data[..]).unwrap();
|
||||||
@@ -161,6 +151,7 @@ fn execute_bad_on_parent() {
|
|||||||
ingress: Vec::new(),
|
ingress: Vec::new(),
|
||||||
},
|
},
|
||||||
&mut DummyExt,
|
&mut DummyExt,
|
||||||
|
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||||
).unwrap_err();
|
).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +183,7 @@ fn processes_messages() {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
&mut DummyExt,
|
&mut DummyExt,
|
||||||
|
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let new_head = HeadData::decode(&mut &ret.head_data[..]).unwrap();
|
let new_head = HeadData::decode(&mut &ret.head_data[..]).unwrap();
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Polkadot is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
mod adder;
|
||||||
|
mod wasm_executor;
|
||||||
|
|
||||||
|
use polkadot_parachain as parachain;
|
||||||
|
use crate::parachain::{
|
||||||
|
MessageRef, UpwardMessageRef,
|
||||||
|
wasm_executor::{Externalities, ExternalitiesError, run_worker},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DummyExt;
|
||||||
|
impl Externalities for DummyExt {
|
||||||
|
fn post_message(&mut self, _message: MessageRef) -> Result<(), ExternalitiesError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn post_upward_message(&mut self, _message: UpwardMessageRef) -> Result<(), ExternalitiesError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not an actual test, but rather an entry point for out-of process WASM executor.
|
||||||
|
// When executing tests the executor spawns currently executing binary, which happens to be test binary.
|
||||||
|
// It then passes "validation_worker" on CLI effectivly making rust test executor to run this single test.
|
||||||
|
#[test]
|
||||||
|
fn validation_worker() {
|
||||||
|
if let Some(id) = std::env::args().find(|a| a.starts_with("/shmem_rs_")) {
|
||||||
|
run_worker(&id).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Polkadot is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Basic parachain that adds a number as part of its state.
|
||||||
|
|
||||||
|
use polkadot_parachain as parachain;
|
||||||
|
use crate::{adder, DummyExt};
|
||||||
|
use crate::parachain::ValidationParams;
|
||||||
|
|
||||||
|
// Code that exposes `validate_block` and loops infinitely
|
||||||
|
const INFINITE_LOOP_CODE: &[u8] = halt::WASM_BINARY;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn terminates_on_timeout() {
|
||||||
|
let result = parachain::wasm_executor::validate_candidate(
|
||||||
|
INFINITE_LOOP_CODE,
|
||||||
|
ValidationParams {
|
||||||
|
parent_head: Default::default(),
|
||||||
|
block_data: Vec::new(),
|
||||||
|
ingress: Vec::new(),
|
||||||
|
},
|
||||||
|
&mut DummyExt,
|
||||||
|
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||||
|
);
|
||||||
|
match result {
|
||||||
|
Err(parachain::wasm_executor::Error::Timeout) => {},
|
||||||
|
r => panic!("{:?}", r),
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that another parachain can validate normaly
|
||||||
|
adder::execute_good_on_parent();
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ use primitives::bytes;
|
|||||||
use primitives::ed25519;
|
use primitives::ed25519;
|
||||||
|
|
||||||
pub use polkadot_parachain::{
|
pub use polkadot_parachain::{
|
||||||
Id, AccountIdConversion, ParachainDispatchOrigin,
|
Id, AccountIdConversion, ParachainDispatchOrigin, UpwardMessage,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Identity that collators use.
|
/// Identity that collators use.
|
||||||
@@ -110,16 +110,6 @@ pub struct Extrinsic {
|
|||||||
pub outgoing_messages: Vec<OutgoingMessage>
|
pub outgoing_messages: Vec<OutgoingMessage>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A message from a parachain to its Relay Chain.
|
|
||||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Debug))]
|
|
||||||
pub struct UpwardMessage {
|
|
||||||
/// The origin for the message to be sent from.
|
|
||||||
pub origin: ParachainDispatchOrigin,
|
|
||||||
/// The message data.
|
|
||||||
pub data: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Candidate receipt type.
|
/// Candidate receipt type.
|
||||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||||
#[cfg_attr(feature = "std", derive(Debug))]
|
#[cfg_attr(feature = "std", derive(Debug))]
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ pub use polkadot_primitives::parachain::{CollatorId, ParachainHost};
|
|||||||
pub use primitives::Blake2Hasher;
|
pub use primitives::Blake2Hasher;
|
||||||
pub use sr_primitives::traits::ProvideRuntimeApi;
|
pub use sr_primitives::traits::ProvideRuntimeApi;
|
||||||
pub use chain_spec::ChainSpec;
|
pub use chain_spec::ChainSpec;
|
||||||
|
pub use consensus::run_validation_worker;
|
||||||
|
|
||||||
/// All configuration for the polkadot node.
|
/// All configuration for the polkadot node.
|
||||||
pub type Configuration = FactoryFullConfiguration<Factory>;
|
pub type Configuration = FactoryFullConfiguration<Factory>;
|
||||||
|
|||||||
@@ -62,5 +62,5 @@ fn main() -> Result<(), cli::error::Error> {
|
|||||||
support_url: "https://github.com/paritytech/polkadot/issues/new",
|
support_url: "https://github.com/paritytech/polkadot/issues/new",
|
||||||
};
|
};
|
||||||
|
|
||||||
cli::run(::std::env::args(), Worker, version)
|
cli::run(Worker, version)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ fn main() {
|
|||||||
id,
|
id,
|
||||||
exit,
|
exit,
|
||||||
key,
|
key,
|
||||||
::std::env::args(),
|
|
||||||
VersionInfo {
|
VersionInfo {
|
||||||
name: "<unknown>",
|
name: "<unknown>",
|
||||||
version: "<unknown>",
|
version: "<unknown>",
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "halt"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
description = "Test parachain which executes forever"
|
||||||
|
edition = "2018"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = [ "std" ]
|
||||||
|
no_std = []
|
||||||
|
std = []
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// Substrate is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Substrate is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
build_current_project_with_rustflags(
|
||||||
|
"wasm_binary.rs",
|
||||||
|
WasmBuilderSource::Crates("1.0.4"),
|
||||||
|
"-C link-arg=--import-memory",
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Polkadot is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Basic parachain that executes forever.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![cfg_attr(feature = "no_std", feature(core_intrinsics, lang_items, core_panic_info, alloc_error_handler))]
|
||||||
|
|
||||||
|
// Make the WASM binary available.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
||||||
|
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
#[panic_handler]
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
|
unsafe {
|
||||||
|
core::intrinsics::abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
#[alloc_error_handler]
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn oom(_: core::alloc::Layout) -> ! {
|
||||||
|
unsafe {
|
||||||
|
core::intrinsics::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn validate_block(params: *const u8, len: usize) -> usize {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ use polkadot_primitives::{Block, Hash, BlockId, Balance, parachain::{
|
|||||||
Id as ParaId, Collation, Extrinsic, OutgoingMessage, UpwardMessage, FeeSchedule,
|
Id as ParaId, Collation, Extrinsic, OutgoingMessage, UpwardMessage, FeeSchedule,
|
||||||
}};
|
}};
|
||||||
use runtime_primitives::traits::ProvideRuntimeApi;
|
use runtime_primitives::traits::ProvideRuntimeApi;
|
||||||
use parachain::{wasm_executor::{self, ExternalitiesError}, MessageRef, UpwardMessageRef};
|
use parachain::{wasm_executor::{self, ExternalitiesError, ExecutionMode}, MessageRef, UpwardMessageRef};
|
||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
@@ -434,7 +434,7 @@ pub fn validate_collation<P>(
|
|||||||
fees_charged: 0,
|
fees_charged: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
match wasm_executor::validate_candidate(&validation_code, params, &mut ext) {
|
match wasm_executor::validate_candidate(&validation_code, params, &mut ext, ExecutionMode::Remote) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
if result.head_data == collation.receipt.head_data.0 {
|
if result.head_data == collation.receipt.head_data.0 {
|
||||||
ext.final_checks(&collation.receipt)
|
ext.final_checks(&collation.receipt)
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ pub use self::shared_table::{
|
|||||||
SharedTable, ParachainWork, PrimedParachainWork, Validated, Statement, SignedStatement,
|
SharedTable, ParachainWork, PrimedParachainWork, Validated, Statement, SignedStatement,
|
||||||
GenericStatement,
|
GenericStatement,
|
||||||
};
|
};
|
||||||
|
pub use parachain::wasm_executor::{run_worker as run_validation_worker};
|
||||||
|
|
||||||
mod attestation_service;
|
mod attestation_service;
|
||||||
mod dynamic_inclusion;
|
mod dynamic_inclusion;
|
||||||
@@ -450,7 +451,7 @@ pub struct ProposerFactory<C, N, P, SC, TxApi: PoolChainApi> {
|
|||||||
key: Arc<ed25519::Pair>,
|
key: Arc<ed25519::Pair>,
|
||||||
_service_handle: ServiceHandle,
|
_service_handle: ServiceHandle,
|
||||||
aura_slot_duration: SlotDuration,
|
aura_slot_duration: SlotDuration,
|
||||||
select_chain: SC,
|
_select_chain: SC,
|
||||||
max_block_data_size: Option<u64>,
|
max_block_data_size: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +470,7 @@ impl<C, N, P, SC, TxApi> ProposerFactory<C, N, P, SC, TxApi> where
|
|||||||
/// Create a new proposer factory.
|
/// Create a new proposer factory.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
client: Arc<P>,
|
client: Arc<P>,
|
||||||
select_chain: SC,
|
_select_chain: SC,
|
||||||
network: N,
|
network: N,
|
||||||
collators: C,
|
collators: C,
|
||||||
transaction_pool: Arc<Pool<TxApi>>,
|
transaction_pool: Arc<Pool<TxApi>>,
|
||||||
@@ -490,7 +491,7 @@ impl<C, N, P, SC, TxApi> ProposerFactory<C, N, P, SC, TxApi> where
|
|||||||
|
|
||||||
let service_handle = crate::attestation_service::start(
|
let service_handle = crate::attestation_service::start(
|
||||||
client,
|
client,
|
||||||
select_chain.clone(),
|
_select_chain.clone(),
|
||||||
parachain_validation.clone(),
|
parachain_validation.clone(),
|
||||||
thread_pool,
|
thread_pool,
|
||||||
key.clone(),
|
key.clone(),
|
||||||
@@ -504,7 +505,7 @@ impl<C, N, P, SC, TxApi> ProposerFactory<C, N, P, SC, TxApi> where
|
|||||||
key,
|
key,
|
||||||
_service_handle: service_handle,
|
_service_handle: service_handle,
|
||||||
aura_slot_duration,
|
aura_slot_duration,
|
||||||
select_chain,
|
_select_chain,
|
||||||
max_block_data_size,
|
max_block_data_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user