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

Commit a14c254

Browse files
committed
Move shared functionality to common, remove worker -> host dep
The worker binaries must be built first so that the host could embed them into `polkadot`. Therefore `pvf/worker` could not depend on `pvf`, so to remove the dependency, common functionality was extracted into `pvf/common`. (NOTE: We already needed to do this host/worker/common separation as part of https://github.com/paritytech/polkadot/issues/7116, it's just unfortunate that it had to be done here and complicate this PR.) Integration tests were moved from `pvf/worker/tests` to `pvf/tests` because they need the PVF host.
1 parent aecc106 commit a14c254

27 files changed

+445
-209
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ root = true
44
indent_style=tab
55
indent_size=tab
66
tab_width=4
7-
max_line_length=120
7+
max_line_length=100
88
end_of_line=lf
99
charset=utf-8
1010
trim_trailing_whitespace=true

Cargo.lock

Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ members = [
8080
"node/core/parachains-inherent",
8181
"node/core/provisioner",
8282
"node/core/pvf",
83+
"node/core/pvf/common",
8384
"node/core/pvf/worker",
8485
"node/core/pvf-checker",
8586
"node/core/runtime-api",

node/core/pvf/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ parity-scale-codec = { version = "3.4.0", default-features = false, features = [
1919

2020
polkadot-parachain = { path = "../../../parachain" }
2121
polkadot-core-primitives = { path = "../../../core-primitives" }
22+
polkadot-node-core-pvf-common = { path = "common" }
23+
# Must depend on the worker because the binaries must have been already built.
24+
polkadot-node-core-pvf-worker = { path = "worker" }
2225
polkadot-node-metrics = { path = "../../metrics" }
2326
polkadot-node-primitives = { path = "../../primitives" }
2427
polkadot-primitives = { path = "../../../primitives" }
@@ -35,3 +38,6 @@ substrate-build-script-utils = { git = "https://github.com/paritytech/substrate"
3538
assert_matches = "1.4.0"
3639
hex-literal = "0.3.4"
3740
tempfile = "3.3.0"
41+
42+
adder = { package = "test-parachain-adder", path = "../../../parachain/test-parachains/adder" }
43+
halt = { package = "test-parachain-halt", path = "../../../parachain/test-parachains/halt" }

node/core/pvf/worker/bin/puppet_worker.rs renamed to node/core/pvf/bin/puppet_worker.rs

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,4 @@
1616

1717
//! Puppet worker used for integration tests.
1818
19-
use sp_tracing;
20-
21-
fn main() {
22-
sp_tracing::try_init_simple();
23-
24-
let args = std::env::args().collect::<Vec<_>>();
25-
if args.len() < 3 {
26-
panic!("wrong number of arguments");
27-
}
28-
29-
let subcommand = &args[1];
30-
match subcommand.as_ref() {
31-
"exit" => {
32-
std::process::exit(1);
33-
},
34-
"sleep" => {
35-
std::thread::sleep(std::time::Duration::from_secs(5));
36-
},
37-
other => panic!("unknown subcommand: {}", other),
38-
}
39-
}
19+
polkadot_node_core_pvf::decl_puppet_worker_main!();

node/core/pvf/common/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "polkadot-node-core-pvf-common"
3+
version.workspace = true
4+
authors.workspace = true
5+
edition.workspace = true
6+
7+
[dependencies]
8+
tokio = { version = "1.24.2", features = ["fs", "process", "io-util"] }
9+
10+
parity-scale-codec = { version = "3.4.0", default-features = false, features = ["derive"] }
11+
12+
polkadot-parachain = { path = "../../../../parachain" }
13+
polkadot-primitives = { path = "../../../../primitives" }
14+
15+
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }

node/core/pvf/common/src/error.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (C) Parity Technologies (UK) Ltd.
2+
// This file is part of Polkadot.
3+
4+
// Polkadot is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// Polkadot is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
16+
17+
use crate::prepare::PrepareStats;
18+
use parity_scale_codec::{Decode, Encode};
19+
use std::fmt;
20+
21+
/// Result of PVF preparation performed by the validation host. Contains stats about the preparation if
22+
/// successful
23+
pub type PrepareResult = Result<PrepareStats, PrepareError>;
24+
25+
/// An error that occurred during the prepare part of the PVF pipeline.
26+
#[derive(Debug, Clone, Encode, Decode)]
27+
pub enum PrepareError {
28+
/// During the prevalidation stage of preparation an issue was found with the PVF.
29+
Prevalidation(String),
30+
/// Compilation failed for the given PVF.
31+
Preparation(String),
32+
/// An unexpected panic has occurred in the preparation worker.
33+
Panic(String),
34+
/// Failed to prepare the PVF due to the time limit.
35+
TimedOut,
36+
/// An IO error occurred. This state is reported by either the validation host or by the worker.
37+
IoErr(String),
38+
/// The temporary file for the artifact could not be created at the given cache path. This state is reported by the
39+
/// validation host (not by the worker).
40+
CreateTmpFileErr(String),
41+
/// The response from the worker is received, but the file cannot be renamed (moved) to the final destination
42+
/// location. This state is reported by the validation host (not by the worker).
43+
RenameTmpFileErr(String),
44+
}
45+
46+
impl PrepareError {
47+
/// Returns whether this is a deterministic error, i.e. one that should trigger reliably. Those
48+
/// errors depend on the PVF itself and the sc-executor/wasmtime logic.
49+
///
50+
/// Non-deterministic errors can happen spuriously. Typically, they occur due to resource
51+
/// starvation, e.g. under heavy load or memory pressure. Those errors are typically transient
52+
/// but may persist e.g. if the node is run by overwhelmingly underpowered machine.
53+
pub fn is_deterministic(&self) -> bool {
54+
use PrepareError::*;
55+
match self {
56+
Prevalidation(_) | Preparation(_) | Panic(_) => true,
57+
TimedOut | IoErr(_) | CreateTmpFileErr(_) | RenameTmpFileErr(_) => false,
58+
}
59+
}
60+
}
61+
62+
impl fmt::Display for PrepareError {
63+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64+
use PrepareError::*;
65+
match self {
66+
Prevalidation(err) => write!(f, "prevalidation: {}", err),
67+
Preparation(err) => write!(f, "preparation: {}", err),
68+
Panic(err) => write!(f, "panic: {}", err),
69+
TimedOut => write!(f, "prepare: timeout"),
70+
IoErr(err) => write!(f, "prepare: io error while receiving response: {}", err),
71+
CreateTmpFileErr(err) => write!(f, "prepare: error creating tmp file: {}", err),
72+
RenameTmpFileErr(err) => write!(f, "prepare: error renaming tmp file: {}", err),
73+
}
74+
}
75+
}

node/core/pvf/common/src/execute.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (C) Parity Technologies (UK) Ltd.
2+
// This file is part of Polkadot.
3+
4+
// Polkadot is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// Polkadot is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
16+
17+
use parity_scale_codec::{Decode, Encode};
18+
use polkadot_parachain::primitives::ValidationResult;
19+
use polkadot_primitives::ExecutorParams;
20+
use std::time::Duration;
21+
22+
/// The payload of the one-time handshake that is done when a worker process is created. Carries
23+
/// data from the host to the worker.
24+
#[derive(Encode, Decode)]
25+
pub struct Handshake {
26+
/// The executor parameters.
27+
pub executor_params: ExecutorParams,
28+
}
29+
30+
/// The response from an execution job on the worker.
31+
#[derive(Encode, Decode)]
32+
pub enum Response {
33+
/// The job completed successfully.
34+
Ok {
35+
/// The result of parachain validation.
36+
result_descriptor: ValidationResult,
37+
/// The amount of CPU time taken by the job.
38+
duration: Duration,
39+
},
40+
/// The candidate is invalid.
41+
InvalidCandidate(String),
42+
/// The job timed out.
43+
TimedOut,
44+
/// Some internal error occurred. Should only be used for errors independent of the candidate.
45+
InternalError(String),
46+
}
47+
48+
impl Response {
49+
/// Creates an invalid response from a context `ctx` and a message `msg` (which can be empty).
50+
pub fn format_invalid(ctx: &'static str, msg: &str) -> Self {
51+
if msg.is_empty() {
52+
Self::InvalidCandidate(ctx.to_string())
53+
} else {
54+
Self::InvalidCandidate(format!("{}: {}", ctx, msg))
55+
}
56+
}
57+
/// Creates an internal response from a context `ctx` and a message `msg` (which can be empty).
58+
pub fn format_internal(ctx: &'static str, msg: &str) -> Self {
59+
if msg.is_empty() {
60+
Self::InternalError(ctx.to_string())
61+
} else {
62+
Self::InternalError(format!("{}: {}", ctx, msg))
63+
}
64+
}
65+
}

node/core/pvf/common/src/lib.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (C) Parity Technologies (UK) Ltd.
2+
// This file is part of Polkadot.
3+
4+
// Polkadot is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// Polkadot is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
16+
17+
pub mod error;
18+
pub mod execute;
19+
pub mod prepare;
20+
pub mod pvf;
21+
22+
use std::mem;
23+
use tokio::io::{self, AsyncRead, AsyncReadExt as _, AsyncWrite, AsyncWriteExt as _};
24+
25+
pub mod tests {
26+
use std::time::Duration;
27+
28+
pub const TEST_EXECUTION_TIMEOUT: Duration = Duration::from_secs(3);
29+
pub const TEST_PREPARATION_TIMEOUT: Duration = Duration::from_secs(30);
30+
}
31+
32+
/// Write some data prefixed by its length into `w`.
33+
pub async fn framed_send(w: &mut (impl AsyncWrite + Unpin), buf: &[u8]) -> io::Result<()> {
34+
let len_buf = buf.len().to_le_bytes();
35+
w.write_all(&len_buf).await?;
36+
w.write_all(buf).await?;
37+
Ok(())
38+
}
39+
40+
/// Read some data prefixed by its length from `r`.
41+
pub async fn framed_recv(r: &mut (impl AsyncRead + Unpin)) -> io::Result<Vec<u8>> {
42+
let mut len_buf = [0u8; mem::size_of::<usize>()];
43+
r.read_exact(&mut len_buf).await?;
44+
let len = usize::from_le_bytes(len_buf);
45+
let mut buf = vec![0; len];
46+
r.read_exact(&mut buf).await?;
47+
Ok(buf)
48+
}

node/core/pvf/common/src/prepare.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (C) Parity Technologies (UK) Ltd.
2+
// This file is part of Polkadot.
3+
4+
// Polkadot is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// Polkadot is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
16+
17+
use parity_scale_codec::{Decode, Encode};
18+
19+
/// Preparation statistics, including the CPU time and memory taken.
20+
#[derive(Debug, Clone, Default, Encode, Decode)]
21+
pub struct PrepareStats {
22+
/// The CPU time that elapsed for the preparation job.
23+
pub cpu_time_elapsed: std::time::Duration,
24+
/// The observed memory statistics for the preparation job.
25+
pub memory_stats: MemoryStats,
26+
}
27+
28+
/// Helper struct to contain all the memory stats, including `MemoryAllocationStats` and, if
29+
/// supported by the OS, `ru_maxrss`.
30+
#[derive(Clone, Debug, Default, Encode, Decode)]
31+
pub struct MemoryStats {
32+
/// Memory stats from `tikv_jemalloc_ctl`.
33+
#[cfg(any(target_os = "linux", feature = "jemalloc-allocator"))]
34+
pub memory_tracker_stats: Option<MemoryAllocationStats>,
35+
/// `ru_maxrss` from `getrusage`. `None` if an error occurred.
36+
#[cfg(target_os = "linux")]
37+
pub max_rss: Option<i64>,
38+
}
39+
40+
/// Statistics of collected memory metrics.
41+
#[cfg(any(target_os = "linux", feature = "jemalloc-allocator"))]
42+
#[derive(Clone, Debug, Default, Encode, Decode)]
43+
pub struct MemoryAllocationStats {
44+
/// Total resident memory, in bytes.
45+
pub resident: u64,
46+
/// Total allocated memory, in bytes.
47+
pub allocated: u64,
48+
}

0 commit comments

Comments
 (0)