1use super::*;
16
17use snarkvm_utilities::DeserializeExt;
18
19pub struct BuildRequest<N: Network> {
20 program: Program<N>,
21 imports: Vec<Program<N>>,
22 function_name: Identifier<N>,
23}
24
25impl<N: Network> BuildRequest<N> {
26 pub const fn new(program: Program<N>, imports: Vec<Program<N>>, function_name: Identifier<N>) -> Self {
28 Self { program, imports, function_name }
29 }
30
31 pub fn send(&self, endpoint: &str) -> Result<BuildResponse<N>> {
33 Ok(ureq::get(endpoint).send_json(self)?.into_json()?)
34 }
35
36 pub const fn program(&self) -> &Program<N> {
38 &self.program
39 }
40
41 pub const fn imports(&self) -> &Vec<Program<N>> {
43 &self.imports
44 }
45
46 pub const fn function_name(&self) -> &Identifier<N> {
48 &self.function_name
49 }
50}
51
52impl<N: Network> Serialize for BuildRequest<N> {
53 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
55 let mut request = serializer.serialize_struct("BuildRequest", 3)?;
56 request.serialize_field("program", &self.program)?;
57 request.serialize_field("imports", &self.imports)?;
58 request.serialize_field("function_name", &self.function_name)?;
59 request.end()
60 }
61}
62
63impl<'de, N: Network> Deserialize<'de> for BuildRequest<N> {
64 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
66 let mut request = serde_json::Value::deserialize(deserializer)?;
68 Ok(Self::new(
70 DeserializeExt::take_from_value::<D>(&mut request, "program")?,
72 DeserializeExt::take_from_value::<D>(&mut request, "imports")?,
74 DeserializeExt::take_from_value::<D>(&mut request, "function_name")?,
76 ))
77 }
78}
79
80pub struct BuildResponse<N: Network> {
81 program_id: ProgramID<N>,
82 function_name: Identifier<N>,
83 proving_key: ProvingKey<N>,
84 verifying_key: VerifyingKey<N>,
85}
86
87impl<N: Network> BuildResponse<N> {
88 pub const fn new(
90 program_id: ProgramID<N>,
91 function_name: Identifier<N>,
92 proving_key: ProvingKey<N>,
93 verifying_key: VerifyingKey<N>,
94 ) -> Self {
95 Self { program_id, function_name, proving_key, verifying_key }
96 }
97
98 pub const fn program_id(&self) -> &ProgramID<N> {
100 &self.program_id
101 }
102
103 pub const fn function_name(&self) -> &Identifier<N> {
105 &self.function_name
106 }
107
108 pub const fn proving_key(&self) -> &ProvingKey<N> {
110 &self.proving_key
111 }
112
113 pub const fn verifying_key(&self) -> &VerifyingKey<N> {
115 &self.verifying_key
116 }
117}
118
119impl<N: Network> Serialize for BuildResponse<N> {
120 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
122 let mut response = serializer.serialize_struct("BuildResponse", 4)?;
123 response.serialize_field("program_id", &self.program_id)?;
124 response.serialize_field("function_name", &self.function_name)?;
125 response.serialize_field("proving_key", &self.proving_key)?;
126 response.serialize_field("verifying_key", &self.verifying_key)?;
127 response.end()
128 }
129}
130
131impl<'de, N: Network> Deserialize<'de> for BuildResponse<N> {
132 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
134 let mut response = serde_json::Value::deserialize(deserializer)?;
136 Ok(Self::new(
138 DeserializeExt::take_from_value::<D>(&mut response, "program_id")?,
140 DeserializeExt::take_from_value::<D>(&mut response, "function_name")?,
142 DeserializeExt::take_from_value::<D>(&mut response, "proving_key")?,
144 DeserializeExt::take_from_value::<D>(&mut response, "verifying_key")?,
146 ))
147 }
148}
149
150impl<N: Network> Package<N> {
151 pub fn build<A: crate::circuit::Aleo<Network = N, BaseField = N::Field>>(
153 &self,
154 endpoint: Option<String>,
155 ) -> Result<()> {
156 if !self.is_build_required::<A>() {
158 return Ok(());
159 }
160
161 let program = self.program();
163 let program_id = program.id();
165
166 #[cfg(feature = "aleo-cli")]
167 println!("⏳ Compiling '{}'...\n", program_id.to_string().bold());
168
169 let build_directory = self.build_directory();
171 if !build_directory.exists() {
173 std::fs::create_dir_all(&build_directory)?;
174 }
175
176 let process = self.get_process()?;
178
179 let imported_programs = program
181 .imports()
182 .keys()
183 .map(|program_id| process.get_program(program_id).cloned())
184 .collect::<Result<Vec<_>>>()?;
185
186 for function_name in program.functions().keys() {
188 match endpoint {
189 Some(ref endpoint) => {
190 let request = BuildRequest::new(program.clone(), imported_programs.clone(), *function_name);
192 let response = request.send(endpoint)?;
194 ensure!(
196 response.program_id() == program_id,
197 "Program ID mismatch: {} != {program_id}",
198 response.program_id()
199 );
200 ensure!(
202 response.function_name() == function_name,
203 "Function name mismatch: {} != {function_name}",
204 response.function_name()
205 );
206 process.insert_proving_key(response.program_id(), function_name, response.proving_key().clone())?;
208 process.insert_verifying_key(
210 response.program_id(),
211 function_name,
212 response.verifying_key().clone(),
213 )?;
214 }
215 None => process.synthesize_key::<A, _>(program_id, function_name, &mut rand::thread_rng())?,
216 }
217 }
218
219 for function_name in program.functions().keys() {
221 let program = process.get_program(program_id)?;
223 let function = program.get_function(function_name)?;
225 for instruction in function.instructions() {
227 if let Instruction::Call(call) = instruction {
228 let (program, resource) = match call.operator() {
230 CallOperator::Locator(locator) => {
231 (process.get_program(locator.program_id())?, locator.resource())
232 }
233 CallOperator::Resource(resource) => (program, resource),
234 };
235 if program.contains_function(resource) {
237 let function_name = resource;
239 let proving_key = process.get_proving_key(program.id(), resource)?;
241 let verifying_key = process.get_verifying_key(program.id(), resource)?;
243
244 let import_build_directory =
246 self.build_directory().join(format!("{}-{}", program.id().name(), program.id().network()));
247 if !import_build_directory.exists() {
249 std::fs::create_dir_all(&import_build_directory)?;
250 }
251
252 let _prover = ProverFile::create(&import_build_directory, function_name, proving_key)?;
254 let _verifier = VerifierFile::create(&import_build_directory, function_name, verifying_key)?;
256 }
257 }
258 }
259
260 let proving_key = process.get_proving_key(program_id, function_name)?;
262 let verifying_key = process.get_verifying_key(program_id, function_name)?;
264
265 let _prover = ProverFile::create(&build_directory, function_name, proving_key)?;
267 let _verifier = VerifierFile::create(&build_directory, function_name, verifying_key)?;
269 }
270
271 let _avm_file = AVMFile::create(&build_directory, program.clone(), true)?;
273
274 if !self.build_directory().exists() {
276 bail!("Build directory does not exist: {}", self.build_directory().display());
277 }
278
279 #[cfg(feature = "aleo-cli")]
280 println!();
281
282 Ok(())
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 type CurrentAleo = snarkvm_circuit::network::AleoV0;
289
290 #[test]
291 fn test_build() {
292 let (directory, package) = crate::package::test_helpers::sample_token_package();
294
295 assert!(!package.build_directory().exists());
297 package.build::<CurrentAleo>(None).unwrap();
299 assert!(package.build_directory().exists());
301
302 std::fs::remove_dir_all(directory).unwrap();
304 }
305
306 #[test]
307 fn test_build_with_import() {
308 let (directory, package) = crate::package::test_helpers::sample_wallet_package();
310
311 assert!(!package.build_directory().exists());
313 package.build::<CurrentAleo>(None).unwrap();
315 assert!(package.build_directory().exists());
317
318 std::fs::remove_dir_all(directory).unwrap();
320 }
321
322 #[test]
323 #[ignore]
324 fn test_build_with_import_credits() {
325 let (directory, package) = crate::package::test_helpers::sample_transfer_package();
327
328 assert!(!package.build_directory().exists());
330 package.build::<CurrentAleo>(None).unwrap();
332 assert!(package.build_directory().exists());
334
335 std::fs::remove_dir_all(directory).unwrap();
337 }
338}