valida_vm_api_linux_x86/
lib.rs

1//! # Valida VM API
2//!
3//! Lita’s Valida zk-VM stack sets a new standard in zero-knowledge proving,
4//! leading in speed, efficiency, modularity and development productivity.
5//!
6//! See [Valida documentation](https://lita.gitbook.io/lita-documentation)
7//!
8//! This crate is a wrapper around `valida` executable and facilitates
9//! usage of `valida` by providing Rust API for
10//! running, proving and verification of Valida programs.
11//!
12//! Two examples are attached.
13//! The first one proves `program1`. The other one proves `program2`.
14//! Their sources and prebuilt versions are included in the crate
15//! in the folder `test_data`.
16//!
17//! `program1` reads an integer from stdin, increments it and prints result to stdout.
18//!
19//! `program2` just unconditionally terminates execution with `__valida_builtin_fail()`.
20//!
21//! # Example 1
22//!
23//! ```
24//! #[cfg(target_arch = "x86_64")]
25//! use valida_vm_api_linux_x86::*;
26//! #[cfg(target_arch = "arm")]
27//! use valida_vm_api_linux_arm::*;
28//! use tempfile::NamedTempFile;
29//! use tmpfile_helper::*;
30//! use std::fs;
31//! use std::path::Path;
32//! use std::default;
33//!
34//! fn main() {
35//!   let program = Path::new("test_data").join("program1");
36//!
37//!   let valida = create_valida().unwrap();
38//!
39//!   // stdin is an ASCII representation of character 'a'
40//!   let stdin = bytes_to_temp_file("a".as_bytes()).unwrap();
41//!   let stdout = NamedTempFile::new().unwrap();
42//!
43//!   let run_status = valida.run(
44//!       &program,
45//!       stdout.as_ref(),
46//!       stdin.as_ref(),
47//!       Default::default());
48//!
49//!   // Check that program terminated with success, i.e. STOP opcode
50//!   assert_eq!(run_status, RunStatus::TerminatedWithStop);
51//!
52//!   let stdout_content = fs::read_to_string(stdout.as_ref()).unwrap();
53//!   // Check that stdout contains ('a' + 1)
54//!   assert_eq!(stdout_content, "b");
55//!
56//!   let proof = NamedTempFile::new().unwrap();
57//!   let prove_status = valida.prove(
58//!       &program, proof.as_ref(),
59//!       stdin.as_ref(),
60//!       Default::default(),
61//!       Default::default());
62//!
63//!   // Proving of a program that terminates with STOP opcode must succeed
64//!   assert_eq!(prove_status, ProveStatus::Success);
65//!
66//!   let verify_status_correct_statement = valida.verify(
67//!       &program,
68//!       proof.as_ref(),
69//!       stdout.as_ref(),
70//!       Default::default(),
71//!       Default::default());
72//!
73//!   // Verification of a program that terminates with STOP opcode and outputs 'b' must succeed
74//!   assert_eq!(verify_status_correct_statement, VerifyStatus::Success);
75//!
76//!   let incorrect_stdout = bytes_to_temp_file("c".as_bytes()).unwrap();
77//!   let verify_status_incorrect_statement = valida.verify(
78//!       &program,
79//!       proof.as_ref(),
80//!       incorrect_stdout.as_ref(),
81//!       Default::default(),
82//!       Default::default());
83//!
84//!   // Verification of a program that terminates with STOP opcode
85//!   // and outputs something else than `b` must fail
86//!   assert_eq!(verify_status_incorrect_statement, VerifyStatus::Failure);
87//! }
88//! ```
89//!
90//! # Example 2
91//!
92//! ```
93//! #[cfg(target_arch = "x86_64")]
94//! use valida_vm_api_linux_x86::*;
95//! #[cfg(target_arch = "arm")]
96//! use valida_vm_api_linux_arm::*;
97//! use tempfile::NamedTempFile;
98//! use tmpfile_helper::*;
99//! use std::fs;
100//! use std::path::Path;
101//! use std::default;
102//!
103//! fn main() {
104//!   let program = Path::new("test_data").join("program2");
105//!
106//!   let valida = create_valida().unwrap();
107//!
108//!   // stdin and stdout do not matter for this program
109//!   let stdin = bytes_to_temp_file("".as_bytes()).unwrap();
110//!   let stdout = NamedTempFile::new().unwrap();
111//!
112//!   let run_status = valida.run(
113//!       &program,
114//!       stdout.as_ref(),
115//!       stdin.as_ref(),
116//!       Default::default());
117//!
118//!   // Check that program terminated with failure, i.e. FAIL opcode
119//!   assert_eq!(run_status, RunStatus::TerminatedWithFail);
120//!
121//!   let proof = NamedTempFile::new().unwrap();
122//!   let prove_status = valida.prove(
123//!       &program,
124//!       proof.as_ref(),
125//!       stdin.as_ref(),
126//!       Default::default(),
127//!       Default::default());
128//!
129//!   // Proving of a program that terminates with STOP opcode may succeed
130//!   assert!(prove_status == ProveStatus::Success || prove_status == ProveStatus::Failure);
131//!
132//!   let verify_status = valida.verify(
133//!       &program,
134//!       proof.as_ref(),
135//!       stdout.as_ref(),
136//!       Default::default(),
137//!       Default::default());
138//!
139//!   // Verification of a program that terminates with FAIL opcode must fail
140//!   assert_eq!(verify_status, VerifyStatus::Failure);
141//! }
142//! ```
143
144use std::fs::remove_file;
145use std::io::{self};
146use std::path::{Path, PathBuf};
147use std::process::{Command, ExitStatus, Stdio};
148
149pub mod optional_arg;
150use optional_arg::*;
151
152mod extract_executable;
153use extract_executable::*;
154
155pub mod tmpfile_helper;
156
157pub use tempfile;
158
159/// The key structure that supports methods for running, proving and verification.
160pub struct Valida {
161    path: PathBuf,
162}
163
164#[derive(PartialEq, Eq, Debug)]
165pub enum RunStatus {
166    TerminatedWithStop,
167    TerminatedWithFail,
168    VMError,
169}
170
171#[derive(PartialEq, Eq, Debug)]
172pub enum ProveStatus {
173    Success,
174    Failure,
175    VMError,
176}
177
178#[derive(PartialEq, Eq, Debug)]
179pub enum VerifyStatus {
180    Success,
181    Failure,
182    VMError,
183}
184
185#[derive(PartialEq, Eq, Debug)]
186pub enum PreprocessStatus {
187    Success,
188    Failure,
189    VMError,
190}
191
192impl Valida {
193    fn execute(
194        &self,
195        command: &str,
196        optional_args: &[&str],
197        mandatory_args: &[&str],
198    ) -> std::io::Result<ExitStatus> {
199        let args = [&[command], optional_args, mandatory_args];
200
201        Command::new(self.path.as_os_str())
202            .args(args.to_vec().concat())
203            .stdin(Stdio::null())
204            .stdout(Stdio::null())
205            .status()
206    }
207
208    /// This is a wrapper for [valida run --fast](https://lita.gitbook.io/lita-documentation/quick-start/valida-zk-vm#executing-input-from-a-file)
209    /// See `create_valida` documentation for example usage.
210    pub fn run(
211        &self,
212        program: &Path,
213        stdout: &Path,
214        stdin: &Path,
215        initial_fp: Option<InitialFp>,
216    ) -> RunStatus {
217        let binding = prepare_args(&[as_dyn_arg(&initial_fp), as_dyn_arg(&Some(FastMode(true)))]);
218        let optional_args: Vec<_> = binding.iter().map(String::as_str).collect();
219
220        let status = self
221            .execute(
222                "run",
223                &optional_args,
224                &[
225                    program.to_str().unwrap(),
226                    stdout.to_str().unwrap(),
227                    stdin.to_str().unwrap(),
228                ],
229            )
230            .map(|t| t.code());
231
232        match status {
233            Ok(Some(code)) => match code {
234                0 => RunStatus::TerminatedWithStop,
235                1 => RunStatus::TerminatedWithFail,
236                _ => RunStatus::VMError,
237            },
238            _ => RunStatus::VMError,
239        }
240    }
241
242    /// This is a wrapper for [valida prove](https://lita.gitbook.io/lita-documentation/quick-start/valida-zk-vm#proving-input-from-a-file)
243    /// See `create_valida` documentation for example usage.
244    pub fn prove(
245        &self,
246        program: &Path,
247        proof: &Path,
248        stdin: &Path,
249        proving_key: Option<ProvingKey>,
250        initial_fp: Option<InitialFp>,
251    ) -> ProveStatus {
252        let binding = prepare_args(&[as_dyn_arg(&proving_key), as_dyn_arg(&initial_fp)]);
253        let optional_args: Vec<_> = binding.iter().map(String::as_str).collect();
254
255        let status = self
256            .execute(
257                "prove",
258                &optional_args,
259                &[
260                    program.to_str().unwrap(),
261                    proof.to_str().unwrap(),
262                    stdin.to_str().unwrap(),
263                ],
264            )
265            .map(|t| t.code());
266
267        match status {
268            Ok(Some(code)) => match code {
269                0 => ProveStatus::Success,
270                _ => ProveStatus::Failure,
271            },
272            _ => ProveStatus::VMError,
273        }
274    }
275
276    /// This is a wrapper for [valida verify](https://lita.gitbook.io/lita-documentation/quick-start/valida-zk-vm#verifying-a-proof)
277    /// See `create_valida` documentation for example usage.
278    pub fn verify(
279        &self,
280        program: &Path,
281        proof: &Path,
282        stdout: &Path,
283        verifying_key: Option<VerifyingKey>,
284        initial_fp: Option<InitialFp>,
285    ) -> VerifyStatus {
286        let binding = prepare_args(&[as_dyn_arg(&verifying_key), as_dyn_arg(&initial_fp)]);
287        let optional_args: Vec<_> = binding.iter().map(String::as_str).collect();
288
289        let status = self
290            .execute(
291                "verify",
292                &optional_args,
293                &[
294                    program.to_str().unwrap(),
295                    proof.to_str().unwrap(),
296                    stdout.to_str().unwrap(),
297                ],
298            )
299            .map(|t| t.code());
300
301        match status {
302            Ok(Some(code)) => match code {
303                0 => VerifyStatus::Success,
304                _ => VerifyStatus::Failure,
305            },
306            _ => VerifyStatus::VMError,
307        }
308    }
309
310    /// This is a wrapper for [valida preprocess](https://lita.gitbook.io/lita-documentation/advanced-usage/zk-vm-advanced-usage#separate-preprocessing-stage)
311    pub fn preprocess(&self, program: &Path, name: String) -> PreprocessStatus {
312        let binding = prepare_args(&[]);
313        let optional_args: Vec<_> = binding.iter().map(String::as_str).collect();
314
315        let status = self
316            .execute(
317                "verify",
318                &optional_args,
319                &[program.to_str().unwrap(), name.as_str()],
320            )
321            .map(|t| t.code());
322
323        match status {
324            Ok(Some(code)) => match code {
325                0 => PreprocessStatus::Success,
326                _ => PreprocessStatus::Failure,
327            },
328            _ => PreprocessStatus::VMError,
329        }
330    }
331}
332
333/// This function creates a wrapper for `valida` executable.
334///
335/// Call this function just once in your program.
336/// Calling it is expensive because it extracts the bundled `valida binary`
337/// and decompresses it to a temporary file.
338pub fn create_valida() -> io::Result<Valida> {
339    let pathbuf = extract_executable()?;
340
341    Ok(Valida { path: pathbuf })
342}
343
344impl Drop for Valida {
345    fn drop(&mut self) {
346        remove_file(self.path.as_mut_os_str()).unwrap();
347    }
348}