From 3d8ceddd82522b875064a9ced6c42f930ed9fb1b Mon Sep 17 00:00:00 2001 From: Andrei Kashin Date: Thu, 8 Feb 2024 16:57:59 +0000 Subject: [PATCH 1/3] Implement zkAsm runner in Rust For now the code is not used anywhere, but we should remove dead_code annotations as soon as we have some usage. --- Cargo.lock | 2 + cranelift/filetests/Cargo.toml | 2 + cranelift/filetests/src/lib.rs | 1 + cranelift/filetests/src/zkasm_runner.rs | 136 ++++++++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 cranelift/filetests/src/zkasm_runner.rs diff --git a/Cargo.lock b/Cargo.lock index 0071b28c4be9..fdf5a62934d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -650,9 +650,11 @@ dependencies = [ "regex", "serde", "serde_derive", + "serde_json", "similar", "smallvec", "target-lexicon", + "tempfile", "thiserror", "toml", "walkdir", diff --git a/cranelift/filetests/Cargo.toml b/cranelift/filetests/Cargo.toml index 262520bacd1c..b7f6bc8867c2 100644 --- a/cranelift/filetests/Cargo.toml +++ b/cranelift/filetests/Cargo.toml @@ -33,6 +33,7 @@ wat.workspace = true toml = { workspace = true } serde = { workspace = true } serde_derive = { workspace = true } +serde_json = { workspace = true } cranelift-wasm.workspace = true wasmparser.workspace = true cranelift.workspace = true @@ -42,3 +43,4 @@ smallvec = { workspace = true } regex = { workspace = true } wasmtime = { workspace = true, features = ["cranelift"] } walkdir = { workspace = true } +tempfile = { workspace = true } diff --git a/cranelift/filetests/src/lib.rs b/cranelift/filetests/src/lib.rs index 24d3e48050dd..726521a5d518 100644 --- a/cranelift/filetests/src/lib.rs +++ b/cranelift/filetests/src/lib.rs @@ -22,6 +22,7 @@ mod match_directive; mod runner; mod runone; mod subtest; +mod zkasm_runner; mod test_alias_analysis; mod test_cat; diff --git a/cranelift/filetests/src/zkasm_runner.rs b/cranelift/filetests/src/zkasm_runner.rs new file mode 100644 index 000000000000..1e1529d136bf --- /dev/null +++ b/cranelift/filetests/src/zkasm_runner.rs @@ -0,0 +1,136 @@ +use serde_derive::Deserialize; +use std::io::Read; +use std::path::Path; +use tempfile::{NamedTempFile, TempDir}; + +#[allow(dead_code)] +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Counters { + cnt_arith: String, + cnt_binary: String, + cnt_keccak_f: String, + cnt_mem_align: String, + cnt_steps: u64, +} + +#[derive(Deserialize, Debug)] +pub enum ExecutionStatus { + #[serde(rename = "pass")] + Success, + #[serde(rename = "runtime error")] + RuntimeError, +} + +#[allow(dead_code)] +#[derive(Deserialize)] +pub struct ExecutionResult { + /// Path to the main zkAsm file that was executed. + path: String, + /// Status of the execution. + status: ExecutionStatus, + /// Error message in case the execution failed. + error: Option, + /// Profiling information about this execution. + /// Only populated for the successful executions. + counters: Option, +} + +impl ExecutionResult { + #[allow(dead_code)] + fn format_error(&self) -> String { + match &self.error { + Some(s) => s.replace("\\n", "\n"), + None => "None".to_string(), + } + } +} + +/// Runs a given snippet of zkAsm code. +#[allow(dead_code)] +pub fn run_zkasm(contents: &str) -> anyhow::Result { + let tmp_dir = TempDir::new()?; + let zkasm_file = tmp_dir.path().join("code.zkasm"); + std::fs::write(&zkasm_file, contents)?; + let mut results = run_zkasm_path(&zkasm_file)?; + assert_eq!(results.len(), 1); + Ok(results.remove(0)) +} + +/// Runs zkAsm at a specified path. +/// If the directory path is passed, all zkAsm files in that directory will be executed. +#[allow(dead_code)] +pub fn run_zkasm_path(input_path: &Path) -> anyhow::Result> { + let dir_path = if input_path.is_dir() { + input_path + } else { + input_path.parent().unwrap() + }; + std::fs::create_dir(dir_path.join("helpers"))?; + let helpers_file = dir_path.join("helpers/2-exp.zkasm"); + std::fs::write( + helpers_file, + include_str!("../../zkasm_data/generated/helpers/2-exp.zkasm"), + )?; + + let mut output_file = NamedTempFile::new()?; + let _ = std::process::Command::new("npm") + .args([ + "--prefix", + "../../tests/zkasm", + "test", + input_path.to_str().unwrap(), + output_file.path().to_str().unwrap(), + ]) + .output()?; + let mut buf = String::new(); + output_file.read_to_string(&mut buf)?; + if buf.is_empty() { + return Ok(Vec::new()); + } + let execution_results = serde_json::from_str(&buf)?; + Ok(execution_results) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_run_zkasm_success() { + let code = r#" +start: + 2 + 3 => A + 5 => B + B: ASSERT +finalizeExecution: + ${beforeLast()} :JMPN(finalizeExecution) + :JMP(start) + "#; + let result = run_zkasm(&code).expect("Failed to run zkAsm"); + assert!( + matches!(result.status, ExecutionStatus::Success), + "Error: {}", + result.format_error() + ); + } + + #[test] + fn test_run_zkasm_runtime_error() { + let code = r#" +start: + 2 + 2 => A + 5 => B + B: ASSERT +finalizeExecution: + ${beforeLast()} :JMPN(finalizeExecution) + :JMP(start) + "#; + let result = run_zkasm(&code).expect("Failed to run zkAsm"); + assert!( + matches!(result.status, ExecutionStatus::RuntimeError), + "Error: {}", + result.format_error() + ); + } +} From bc7105bf6e07509b826e2e2c89add3000bf1afb5 Mon Sep 17 00:00:00 2001 From: Andrei Kashin Date: Mon, 12 Feb 2024 12:33:41 +0000 Subject: [PATCH 2/3] Return error message if `npm` fails --- cranelift/filetests/src/zkasm_runner.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cranelift/filetests/src/zkasm_runner.rs b/cranelift/filetests/src/zkasm_runner.rs index 1e1529d136bf..25fade81929f 100644 --- a/cranelift/filetests/src/zkasm_runner.rs +++ b/cranelift/filetests/src/zkasm_runner.rs @@ -74,7 +74,7 @@ pub fn run_zkasm_path(input_path: &Path) -> anyhow::Result> )?; let mut output_file = NamedTempFile::new()?; - let _ = std::process::Command::new("npm") + let output = std::process::Command::new("npm") .args([ "--prefix", "../../tests/zkasm", @@ -83,6 +83,13 @@ pub fn run_zkasm_path(input_path: &Path) -> anyhow::Result> output_file.path().to_str().unwrap(), ]) .output()?; + if !output.status.success() { + return Err(anyhow::anyhow!( + "Failed to run `npm`: {}; stderr: {}", + output.status, + String::from_utf8(output.stderr)? + )); + } let mut buf = String::new(); output_file.read_to_string(&mut buf)?; if buf.is_empty() { From ee06522a75f47cef26942be420cbfcb05fac6363 Mon Sep 17 00:00:00 2001 From: Andrei Kashin Date: Mon, 12 Feb 2024 12:43:38 +0000 Subject: [PATCH 3/3] Install node for Rust tests --- .github/workflows/main.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 66c80d7f865e..e636c4f0045b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -409,6 +409,12 @@ jobs: - uses: ./.github/actions/install-rust with: toolchain: ${{ matrix.rust }} + - uses: actions/setup-node@v3 + with: + node-version: '20.8.0' + cache: 'npm' + cache-dependency-path: tests/zkasm/package-lock.json + - run: npm ci --prefix tests/zkasm # Install targets in order to build various tests throughout the repo - run: rustup target add wasm32-wasi wasm32-unknown-unknown ${{ matrix.target }}