snarkvm_debug/file/
verifier.rs1use crate::{
16 prelude::{FromBytes, Identifier, IoResult, Network, Read, ToBytes},
17 synthesizer::{snark::VerifyingKey, Program},
18};
19
20use anyhow::{anyhow, bail, ensure, Result};
21use std::{
22 fs::{self, File},
23 io::Write,
24 path::Path,
25};
26
27static VERIFIER_FILE_EXTENSION: &str = "verifier";
28
29pub struct VerifierFile<N: Network> {
30 function_name: Identifier<N>,
32 verifying_key: VerifyingKey<N>,
34}
35
36impl<N: Network> VerifierFile<N> {
37 pub fn create(directory: &Path, function_name: &Identifier<N>, verifying_key: VerifyingKey<N>) -> Result<Self> {
39 ensure!(directory.exists(), "The build directory does not exist: '{}'", directory.display());
41 ensure!(!Program::is_reserved_keyword(function_name), "Function name is invalid (reserved): {}", function_name);
43
44 let verifier_file = Self { function_name: *function_name, verifying_key };
46
47 let file_name = format!("{function_name}.{VERIFIER_FILE_EXTENSION}");
49 let path = directory.join(file_name);
51 File::create(&path)?.write_all(&verifier_file.to_bytes_le()?)?;
53
54 Self::from_filepath(&path)
56 }
57
58 pub fn open(directory: &Path, function_name: &Identifier<N>) -> Result<Self> {
60 ensure!(directory.exists(), "The build directory does not exist: '{}'", directory.display());
62
63 let file_name = format!("{function_name}.{VERIFIER_FILE_EXTENSION}");
65 let path = directory.join(file_name);
67 ensure!(path.exists(), "The verifier file is missing: '{}'", path.display());
69
70 let verifier = Self::from_filepath(&path)?;
72
73 if verifier.function_name() != function_name {
75 bail!(
76 "The verifier file for '{}' contains an incorrect function name of '{}'",
77 function_name,
78 verifier.function_name()
79 );
80 }
81
82 Ok(verifier)
83 }
84
85 pub fn exists_at(directory: &Path, function_name: &Identifier<N>) -> bool {
87 let file_name = format!("{function_name}.{VERIFIER_FILE_EXTENSION}");
89 let path = directory.join(file_name);
91 Self::check_path(&path).is_ok() && path.exists()
93 }
94
95 pub const fn function_name(&self) -> &Identifier<N> {
97 &self.function_name
98 }
99
100 pub const fn verifying_key(&self) -> &VerifyingKey<N> {
102 &self.verifying_key
103 }
104
105 pub fn remove(&self, path: &Path) -> Result<()> {
107 if !path.exists() {
109 Ok(())
110 } else {
111 Self::check_path(path)?;
113 Ok(fs::remove_file(path)?)
115 }
116 }
117}
118
119impl<N: Network> VerifierFile<N> {
120 fn check_path(path: &Path) -> Result<()> {
122 ensure!(path.is_file(), "The path is not a file.");
124
125 let extension = path.extension().ok_or_else(|| anyhow!("File extension not found."))?;
127 ensure!(extension == VERIFIER_FILE_EXTENSION, "File extension is incorrect.");
128
129 ensure!(path.exists(), "File does not exist: {}", path.display());
131
132 Ok(())
133 }
134
135 fn from_filepath(file: &Path) -> Result<Self> {
137 Self::check_path(file)?;
139 let verifier = Self::from_bytes_le(&fs::read(file)?)?;
141
142 let file_stem = file
144 .file_stem()
145 .ok_or_else(|| anyhow!("File name not found."))?
146 .to_str()
147 .ok_or_else(|| anyhow!("File name not found."))?
148 .to_string();
149 ensure!(verifier.function_name.to_string() == file_stem, "Function name does not match file stem.");
151
152 Ok(verifier)
154 }
155
156 pub fn write_to(&self, path: &Path) -> Result<()> {
158 Self::check_path(path)?;
160
161 let file_stem = path
163 .file_name()
164 .ok_or_else(|| anyhow!("File name not found."))?
165 .to_str()
166 .ok_or_else(|| anyhow!("File name not found."))?
167 .to_string();
168 ensure!(self.function_name.to_string() == file_stem, "Function name does not match file stem.");
170
171 Ok(File::create(path)?.write_all(&self.to_bytes_le()?)?)
173 }
174}
175
176impl<N: Network> FromBytes for VerifierFile<N> {
177 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
179 let function_name = Identifier::read_le(&mut reader)?;
180 let verifying_key = FromBytes::read_le(&mut reader)?;
181 Ok(Self { function_name, verifying_key })
182 }
183}
184
185impl<N: Network> ToBytes for VerifierFile<N> {
186 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
188 self.function_name.write_le(&mut writer)?;
189 self.verifying_key.write_le(&mut writer)
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use crate::{
197 prelude::{FromStr, Parser, TestRng},
198 synthesizer::Process,
199 };
200
201 type CurrentNetwork = snarkvm_console::network::Testnet3;
202 type CurrentAleo = snarkvm_circuit::AleoV0;
203
204 fn temp_dir() -> std::path::PathBuf {
205 tempfile::tempdir().expect("Failed to open temporary directory").into_path()
206 }
207
208 #[test]
209 fn test_create_and_open() {
210 let directory = temp_dir();
212
213 let program_string = r"
214program token.aleo;
215
216record token:
217 owner as address.private;
218 token_amount as u64.private;
219
220function compute:
221 input r0 as token.record;
222 add r0.token_amount r0.token_amount into r1;
223 output r1 as u64.private;";
224
225 let (string, program) = Program::<CurrentNetwork>::parse(program_string).unwrap();
227 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
228
229 let mut process = Process::load().unwrap();
231 process.add_program(&program).unwrap();
233
234 let function_name = Identifier::from_str("compute").unwrap();
236
237 process.synthesize_key::<CurrentAleo, _>(program.id(), &function_name, &mut TestRng::default()).unwrap();
239
240 let verifying_key = process.get_verifying_key(program.id(), function_name).unwrap();
242
243 let expected = VerifierFile::create(&directory, &function_name, verifying_key).unwrap();
245 let candidate = VerifierFile::open(&directory, &function_name).unwrap();
247 assert_eq!(expected.to_bytes_le().unwrap(), candidate.to_bytes_le().unwrap());
249 }
250}