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}