PVF: more filesystem sandboxing (#1373)

This commit is contained in:
Marcin S
2023-09-28 18:24:29 +02:00
committed by GitHub
parent de71fecc4e
commit c1eb342b14
24 changed files with 1528 additions and 612 deletions
+35 -1
View File
@@ -100,7 +100,7 @@ async fn execute_bad_block_on_parent() {
let host = TestHost::new();
let _ret = host
let _err = host
.validate_candidate(
adder::wasm_binary_unwrap(),
ValidationParams {
@@ -145,3 +145,37 @@ async fn stress_spawn() {
futures::future::join_all((0..100).map(|_| execute(host.clone()))).await;
}
// With one worker, run multiple execution jobs serially. They should not conflict.
#[tokio::test]
async fn execute_can_run_serially() {
let host = std::sync::Arc::new(TestHost::new_with_config(|cfg| {
cfg.execute_workers_max_num = 1;
}));
async fn execute(host: std::sync::Arc<TestHost>) {
let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) };
let block_data = BlockData { state: 0, add: 512 };
let ret = host
.validate_candidate(
adder::wasm_binary_unwrap(),
ValidationParams {
parent_head: GenericHeadData(parent_head.encode()),
block_data: GenericBlockData(block_data.encode()),
relay_parent_number: 1,
relay_parent_storage_root: Default::default(),
},
Default::default(),
)
.await
.unwrap();
let new_head = HeadData::decode(&mut &ret.head_data.0[..]).unwrap();
assert_eq!(new_head.number, 1);
assert_eq!(new_head.parent_hash, parent_head.hash());
assert_eq!(new_head.post_state, hash_state(512));
}
futures::future::join_all((0..5).map(|_| execute(host.clone()))).await;
}
+51 -4
View File
@@ -18,8 +18,8 @@
use assert_matches::assert_matches;
use parity_scale_codec::Encode as _;
use polkadot_node_core_pvf::{
start, Config, InvalidCandidate, Metrics, PrepareJobKind, PvfPrepData, ValidationError,
ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR,
start, Config, InvalidCandidate, Metrics, PrepareError, PrepareJobKind, PrepareStats,
PvfPrepData, ValidationError, ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR,
};
use polkadot_parachain_primitives::primitives::{BlockData, ValidationParams, ValidationResult};
use polkadot_primitives::ExecutorParams;
@@ -70,6 +70,33 @@ impl TestHost {
Self { cache_dir, host: Mutex::new(host) }
}
async fn precheck_pvf(
&self,
code: &[u8],
executor_params: ExecutorParams,
) -> Result<PrepareStats, PrepareError> {
let (result_tx, result_rx) = futures::channel::oneshot::channel();
let code = sp_maybe_compressed_blob::decompress(code, 16 * 1024 * 1024)
.expect("Compression works");
self.host
.lock()
.await
.precheck_pvf(
PvfPrepData::from_code(
code.into(),
executor_params,
TEST_PREPARATION_TIMEOUT,
PrepareJobKind::Prechecking,
),
result_tx,
)
.await
.unwrap();
result_rx.await.unwrap()
}
async fn validate_candidate(
&self,
code: &[u8],
@@ -291,8 +318,12 @@ async fn deleting_prepared_artifact_does_not_dispute() {
{
// Get the artifact path (asserting it exists).
let mut cache_dir: Vec<_> = std::fs::read_dir(cache_dir).unwrap().collect();
assert_eq!(cache_dir.len(), 1);
let artifact_path = cache_dir.pop().unwrap().unwrap();
// Should contain the artifact and the worker dir.
assert_eq!(cache_dir.len(), 2);
let mut artifact_path = cache_dir.pop().unwrap().unwrap();
if artifact_path.path().is_dir() {
artifact_path = cache_dir.pop().unwrap().unwrap();
}
// Delete the artifact.
std::fs::remove_file(artifact_path.path()).unwrap();
@@ -317,3 +348,19 @@ async fn deleting_prepared_artifact_does_not_dispute() {
r => panic!("{:?}", r),
}
}
// With one worker, run multiple preparation jobs serially. They should not conflict.
#[tokio::test]
async fn prepare_can_run_serially() {
let host = TestHost::new_with_config(|cfg| {
cfg.prepare_workers_hard_max_num = 1;
});
let _stats = host
.precheck_pvf(::adder::wasm_binary_unwrap(), Default::default())
.await
.unwrap();
// Prepare a different wasm blob to prevent skipping work.
let _stats = host.precheck_pvf(halt::wasm_binary_unwrap(), Default::default()).await.unwrap();
}
@@ -14,8 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use polkadot_node_core_pvf::testing::{spawn_with_program_path, SpawnErr};
use std::time::Duration;
use polkadot_node_core_pvf::{
testing::{spawn_with_program_path, SpawnErr},
SecurityStatus,
};
use std::{env, time::Duration};
fn worker_path(name: &str) -> std::path::PathBuf {
let mut worker_path = std::env::current_exe().unwrap();
@@ -33,8 +36,10 @@ async fn spawn_immediate_exit() {
let result = spawn_with_program_path(
"integration-test",
worker_path("polkadot-prepare-worker"),
&env::temp_dir(),
&["exit"],
Duration::from_secs(2),
SecurityStatus::default(),
)
.await;
assert!(matches!(result, Err(SpawnErr::AcceptTimeout)));
@@ -45,8 +50,10 @@ async fn spawn_timeout() {
let result = spawn_with_program_path(
"integration-test",
worker_path("polkadot-execute-worker"),
&env::temp_dir(),
&["test-sleep"],
Duration::from_secs(2),
SecurityStatus::default(),
)
.await;
assert!(matches!(result, Err(SpawnErr::AcceptTimeout)));
@@ -57,8 +64,10 @@ async fn should_connect() {
let _ = spawn_with_program_path(
"integration-test",
worker_path("polkadot-prepare-worker"),
&env::temp_dir(),
&["prepare-worker"],
Duration::from_secs(2),
SecurityStatus::default(),
)
.await
.unwrap();