referrerpolicy=no-referrer-when-downgrade

polkadot_node_core_pvf/
testing.rs

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//! Various utilities for testing.
18
19pub use crate::{
20	host::{EXECUTE_BINARY_NAME, PREPARE_BINARY_NAME},
21	worker_interface::{spawn_with_program_path, SpawnErr},
22};
23
24use crate::{artifacts::ArtifactId, get_worker_version};
25use is_executable::IsExecutable;
26use polkadot_node_core_pvf_common::pvf::PvfPrepData;
27use polkadot_node_primitives::NODE_VERSION;
28use polkadot_primitives::ExecutorParams;
29use std::{
30	path::PathBuf,
31	sync::{Mutex, OnceLock},
32};
33
34/// A function that emulates the stitches together behaviors of the preparation and the execution
35/// worker in a single synchronous function.
36pub fn validate_candidate(
37	code: &[u8],
38	params: &[u8],
39) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
40	use polkadot_node_core_pvf_common::executor_interface::{prepare, prevalidate};
41	use polkadot_node_core_pvf_execute_worker::execute_artifact;
42
43	let code = sp_maybe_compressed_blob::decompress(code, 10 * 1024 * 1024)
44		.expect("Decompressing code failed");
45
46	let blob = prevalidate(&code)?;
47	let executor_params = ExecutorParams::default();
48	let compiled_artifact_blob = prepare(blob, &executor_params)?;
49
50	let result = unsafe {
51		// SAFETY: This is trivially safe since the artifact is obtained by calling `prepare`
52		//         and is written into a temporary directory in an unmodified state.
53		execute_artifact(&compiled_artifact_blob, &executor_params, params)?
54	};
55
56	Ok(result)
57}
58
59/// Retrieves the worker paths and builds workers as needed.
60///
61/// NOTE: This should only be called in dev code (tests, benchmarks) as it relies on the relative
62/// paths of the built workers.
63pub fn build_workers_and_get_paths() -> (PathBuf, PathBuf) {
64	// Only needs to be called once for the current process.
65	static WORKER_PATHS: OnceLock<Mutex<(PathBuf, PathBuf)>> = OnceLock::new();
66
67	fn build_workers() {
68		let mut build_args = vec![
69			"build",
70			"--package=polkadot",
71			"--bin=polkadot-prepare-worker",
72			"--bin=polkadot-execute-worker",
73		];
74
75		if cfg!(build_profile = "release") {
76			build_args.push("--release");
77		}
78
79		let mut cargo = std::process::Command::new("cargo");
80		let cmd = cargo
81			// wasm runtime not needed
82			.env("SKIP_WASM_BUILD", "1")
83			.args(build_args)
84			.stdout(std::process::Stdio::piped());
85
86		println!("INFO: calling `{cmd:?}`");
87		let exit_status = cmd.status().expect("Failed to run the build program");
88
89		if !exit_status.success() {
90			eprintln!("ERROR: Failed to build workers: {}", exit_status.code().unwrap());
91			std::process::exit(1);
92		}
93	}
94
95	let mutex = WORKER_PATHS.get_or_init(|| {
96		let mut workers_path = std::env::current_exe().unwrap();
97		workers_path.pop();
98		workers_path.pop();
99		let mut prepare_worker_path = workers_path.clone();
100		prepare_worker_path.push(PREPARE_BINARY_NAME);
101		let mut execute_worker_path = workers_path.clone();
102		execute_worker_path.push(EXECUTE_BINARY_NAME);
103
104		// explain why a build happens
105		if !prepare_worker_path.is_executable() {
106			println!("WARN: Prepare worker does not exist or is not executable. Workers directory: {:?}", workers_path);
107		}
108		if !execute_worker_path.is_executable() {
109			println!("WARN: Execute worker does not exist or is not executable. Workers directory: {:?}", workers_path);
110		}
111		if let Ok(ver) = get_worker_version(&prepare_worker_path) {
112			if ver != NODE_VERSION {
113				println!("WARN: Prepare worker version {ver} does not match node version {NODE_VERSION}; worker path: {prepare_worker_path:?}");
114			}
115		}
116		if let Ok(ver) = get_worker_version(&execute_worker_path) {
117			if ver != NODE_VERSION {
118				println!("WARN: Execute worker version {ver} does not match node version {NODE_VERSION}; worker path: {execute_worker_path:?}");
119			}
120		}
121
122		build_workers();
123
124		Mutex::new((prepare_worker_path, execute_worker_path))
125	});
126
127	let guard = mutex.lock().unwrap();
128	(guard.0.clone(), guard.1.clone())
129}
130
131/// Creates a new PVF which artifact id can be uniquely identified by the given number.
132pub fn artifact_id(discriminator: u32) -> ArtifactId {
133	ArtifactId::from_pvf_prep_data(&PvfPrepData::from_discriminator(discriminator))
134}