Skip to content

Commit

Permalink
Merge pull request #213 from near/zkasm_runner
Browse files Browse the repository at this point in the history
Implement simple zkasm runner in Rust
  • Loading branch information
aborg-dev authored Feb 12, 2024
2 parents b9c037c + ee06522 commit e360122
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cranelift/filetests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -42,3 +43,4 @@ smallvec = { workspace = true }
regex = { workspace = true }
wasmtime = { workspace = true, features = ["cranelift"] }
walkdir = { workspace = true }
tempfile = { workspace = true }
1 change: 1 addition & 0 deletions cranelift/filetests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod match_directive;
mod runner;
mod runone;
mod subtest;
mod zkasm_runner;

mod test_alias_analysis;
mod test_cat;
Expand Down
143 changes: 143 additions & 0 deletions cranelift/filetests/src/zkasm_runner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
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<String>,
/// Profiling information about this execution.
/// Only populated for the successful executions.
counters: Option<Counters>,
}

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<ExecutionResult> {
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<Vec<ExecutionResult>> {
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 output = std::process::Command::new("npm")
.args([
"--prefix",
"../../tests/zkasm",
"test",
input_path.to_str().unwrap(),
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() {
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()
);
}
}

0 comments on commit e360122

Please sign in to comment.