1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (C) 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/>.

//! Various utilities for testing.

pub use crate::{
	host::{EXECUTE_BINARY_NAME, PREPARE_BINARY_NAME},
	worker_interface::{spawn_with_program_path, SpawnErr},
};

use crate::{artifacts::ArtifactId, get_worker_version};
use is_executable::IsExecutable;
use polkadot_node_core_pvf_common::pvf::PvfPrepData;
use polkadot_node_primitives::NODE_VERSION;
use polkadot_primitives::ExecutorParams;
use std::{
	path::PathBuf,
	sync::{Mutex, OnceLock},
};

/// A function that emulates the stitches together behaviors of the preparation and the execution
/// worker in a single synchronous function.
pub fn validate_candidate(
	code: &[u8],
	params: &[u8],
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
	use polkadot_node_core_pvf_common::executor_interface::{prepare, prevalidate};
	use polkadot_node_core_pvf_execute_worker::execute_artifact;

	let code = sp_maybe_compressed_blob::decompress(code, 10 * 1024 * 1024)
		.expect("Decompressing code failed");

	let blob = prevalidate(&code)?;
	let executor_params = ExecutorParams::default();
	let compiled_artifact_blob = prepare(blob, &executor_params)?;

	let result = unsafe {
		// SAFETY: This is trivially safe since the artifact is obtained by calling `prepare`
		//         and is written into a temporary directory in an unmodified state.
		execute_artifact(&compiled_artifact_blob, &executor_params, params)?
	};

	Ok(result)
}

/// Retrieves the worker paths and builds workers as needed.
///
/// NOTE: This should only be called in dev code (tests, benchmarks) as it relies on the relative
/// paths of the built workers.
pub fn build_workers_and_get_paths() -> (PathBuf, PathBuf) {
	// Only needs to be called once for the current process.
	static WORKER_PATHS: OnceLock<Mutex<(PathBuf, PathBuf)>> = OnceLock::new();

	fn build_workers() {
		let mut build_args = vec![
			"build",
			"--package=polkadot",
			"--bin=polkadot-prepare-worker",
			"--bin=polkadot-execute-worker",
		];

		if cfg!(build_type = "release") {
			build_args.push("--release");
		}

		let mut cargo = std::process::Command::new("cargo");
		let cmd = cargo
			// wasm runtime not needed
			.env("SKIP_WASM_BUILD", "1")
			.args(build_args)
			.stdout(std::process::Stdio::piped());

		println!("INFO: calling `{cmd:?}`");
		let exit_status = cmd.status().expect("Failed to run the build program");

		if !exit_status.success() {
			eprintln!("ERROR: Failed to build workers: {}", exit_status.code().unwrap());
			std::process::exit(1);
		}
	}

	let mutex = WORKER_PATHS.get_or_init(|| {
		let mut workers_path = std::env::current_exe().unwrap();
		workers_path.pop();
		workers_path.pop();
		let mut prepare_worker_path = workers_path.clone();
		prepare_worker_path.push(PREPARE_BINARY_NAME);
		let mut execute_worker_path = workers_path.clone();
		execute_worker_path.push(EXECUTE_BINARY_NAME);

		// explain why a build happens
		if !prepare_worker_path.is_executable() {
			println!("WARN: Prepare worker does not exist or is not executable. Workers directory: {:?}", workers_path);
		}
		if !execute_worker_path.is_executable() {
			println!("WARN: Execute worker does not exist or is not executable. Workers directory: {:?}", workers_path);
		}
		if let Ok(ver) = get_worker_version(&prepare_worker_path) {
			if ver != NODE_VERSION {
				println!("WARN: Prepare worker version {ver} does not match node version {NODE_VERSION}; worker path: {prepare_worker_path:?}");
			}
		}
		if let Ok(ver) = get_worker_version(&execute_worker_path) {
			if ver != NODE_VERSION {
				println!("WARN: Execute worker version {ver} does not match node version {NODE_VERSION}; worker path: {execute_worker_path:?}");
			}
		}

		build_workers();

		Mutex::new((prepare_worker_path, execute_worker_path))
	});

	let guard = mutex.lock().unwrap();
	(guard.0.clone(), guard.1.clone())
}

/// Creates a new PVF which artifact id can be uniquely identified by the given number.
pub fn artifact_id(discriminator: u32) -> ArtifactId {
	ArtifactId::from_pvf_prep_data(&PvfPrepData::from_discriminator(discriminator))
}