1use super::*;
17
18impl<N: Network> Stack<N> {
19 pub fn execute_closure<A: circuit::Aleo<Network = N>>(
24 &self,
25 closure: &Closure<N>,
26 inputs: &[circuit::Value<A>],
27 call_stack: CallStack<N>,
28 signer: circuit::Address<A>,
29 caller: circuit::Address<A>,
30 tvk: circuit::Field<A>,
31 ) -> Result<Vec<circuit::Value<A>>> {
32 let timer = timer!("Stack::execute_closure");
33
34 ensure!(!matches!(call_stack, CallStack::Evaluate(..)), "Illegal operation: cannot evaluate in execute mode");
36
37 if closure.inputs().len() != inputs.len() {
39 bail!("Expected {} inputs, found {}", closure.inputs().len(), inputs.len())
40 }
41 lap!(timer, "Check the number of inputs");
42
43 let num_public = A::num_public();
45
46 let mut registers = Registers::new(call_stack, self.get_register_types(closure.name())?.clone());
48 registers.set_signer_circuit(signer);
50 registers.set_caller_circuit(caller);
52 registers.set_tvk_circuit(tvk);
54 lap!(timer, "Initialize the registers");
55
56 closure.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| {
58 if let CallStack::Execute(..) = registers.call_stack_ref() {
60 use circuit::Eject;
61 registers.store(self, register, input.eject_value())?;
63 }
64 registers.store_circuit(self, register, input.clone())
66 })?;
67 lap!(timer, "Store the inputs");
68
69 for instruction in closure.instructions() {
71 if let CallStack::Execute(..) = registers.call_stack_ref() {
73 if let Err(error) = instruction.evaluate(self, &mut registers) {
75 bail!("Failed to evaluate instruction ({instruction}): {error}");
76 }
77 }
78 instruction.execute(self, &mut registers)?;
80 }
81 lap!(timer, "Execute the instructions");
82
83 ensure!(A::num_public() == num_public, "Illegal closure operation: instructions injected public variables");
85
86 use circuit::Inject;
87
88 let outputs = closure
90 .outputs()
91 .iter()
92 .map(|output| {
93 match output.operand() {
94 Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
96 circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
97 ))),
98 Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
100 Operand::ProgramID(program_id) => {
102 Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address(
103 circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?),
104 ))))
105 }
106 Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
108 circuit::Literal::Address(registers.signer_circuit()?),
109 ))),
110 Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
112 circuit::Literal::Address(registers.caller_circuit()?),
113 ))),
114 Operand::BlockHeight => {
116 bail!("Illegal operation: cannot retrieve the block height in a closure scope")
117 }
118 Operand::NetworkID => {
120 bail!("Illegal operation: cannot retrieve the network id in a closure scope")
121 }
122 Operand::Checksum(_) => bail!("Illegal operation: cannot retrieve the checksum in a closure scope"),
124 Operand::Edition(_) => bail!("Illegal operation: cannot retrieve the edition in a closure scope"),
126 Operand::ProgramOwner(_) => {
128 bail!("Illegal operation: cannot retrieve the program owner in a closure scope")
129 }
130 }
131 })
132 .collect();
133 lap!(timer, "Load the outputs");
134
135 finish!(timer);
136 outputs
137 }
138
139 pub fn execute_function<A: circuit::Aleo<Network = N>, R: CryptoRng + Rng>(
146 &self,
147 mut call_stack: CallStack<N>,
148 console_caller: Option<ProgramID<N>>,
149 console_root_tvk: Option<Field<N>>,
150 rng: &mut R,
151 ) -> Result<Response<N>> {
152 let timer = timer!("Stack::execute_function");
153
154 A::initialize_global_constants();
156 A::reset();
158
159 if let CallStack::CheckDeployment(_, _, _, constraint_limit, variable_limit) = &call_stack {
162 A::set_constraint_limit(*constraint_limit);
163 A::set_variable_limit(*variable_limit);
164 }
165
166 let console_request = call_stack.pop()?;
168
169 ensure!(
171 **console_request.network_id() == N::ID,
172 "Network ID mismatch. Expected {}, but found {}",
173 N::ID,
174 console_request.network_id()
175 );
176
177 ensure!(console_caller.is_some() == console_root_tvk.is_some());
179 let console_is_root = console_caller.is_none();
181
182 let console_parent = match console_caller {
186 None => console_request.program_id().to_address()?,
188 Some(console_caller) => console_caller.to_address()?,
190 };
191
192 let function = self.get_function(console_request.function_name())?;
194 let num_inputs = function.inputs().len();
196 if num_inputs != console_request.inputs().len() {
198 bail!("Expected {num_inputs} inputs, found {}", console_request.inputs().len())
199 }
200 let input_types = function.input_types();
202 let output_types = function.output_types();
204 lap!(timer, "Retrieve the input and output types");
205
206 console_request.inputs().iter().zip_eq(&input_types).try_for_each(|(input, input_type)| {
208 self.matches_value_type(input, input_type)
210 })?;
211 lap!(timer, "Verify the input types");
212
213 let program_checksum = match self.program().contains_constructor() {
215 true => Some(self.program_checksum_as_field()?),
216 false => None,
217 };
218
219 ensure!(
221 console_request.verify(&input_types, console_is_root, program_checksum),
222 "[Execute] Request is invalid"
223 );
224 lap!(timer, "Verify the console request");
225
226 let mut registers = Registers::new(call_stack, self.get_register_types(function.name())?.clone());
228
229 let console_root_tvk = console_root_tvk.unwrap_or(*console_request.tvk());
231 let root_tvk = circuit::Field::<A>::new(circuit::Mode::Private, console_root_tvk);
233 registers.set_root_tvk(console_root_tvk);
235 registers.set_root_tvk_circuit(root_tvk.clone());
237
238 let program_checksum = program_checksum.map(|c| circuit::Field::<A>::new(circuit::Mode::Public, c));
240
241 use circuit::{Eject, Inject};
242
243 let tpk = circuit::Group::<A>::new(circuit::Mode::Public, console_request.to_tpk());
245 let request = circuit::Request::new(circuit::Mode::Private, console_request.clone());
247
248 let is_root = circuit::Boolean::new(circuit::Mode::Public, console_is_root);
250 let parent = circuit::Address::new(circuit::Mode::Public, console_parent);
252 let caller = Ternary::ternary(&is_root, request.signer(), &parent);
254
255 A::assert(request.verify(&input_types, &tpk, Some(root_tvk), is_root, program_checksum));
257 lap!(timer, "Verify the circuit request");
258
259 registers.set_signer(*console_request.signer());
261 registers.set_signer_circuit(request.signer().clone());
263
264 registers.set_caller(caller.eject_value());
266 registers.set_caller_circuit(caller);
268
269 registers.set_tvk(*console_request.tvk());
271 registers.set_tvk_circuit(request.tvk().clone());
273
274 lap!(timer, "Initialize the registers");
275
276 Self::log_circuit::<A>("Request");
277
278 let num_request_constraints = A::num_constraints();
280
281 let num_public = A::num_public();
283
284 function.inputs().iter().map(|i| i.register()).zip_eq(request.inputs()).try_for_each(|(register, input)| {
286 if let CallStack::Execute(..) = registers.call_stack_ref() {
288 registers.store(self, register, input.eject_value())?;
290 }
291 registers.store_circuit(self, register, input.clone())
293 })?;
294 lap!(timer, "Store the inputs");
295
296 let mut contains_function_call = false;
298
299 for instruction in function.instructions() {
301 if let CallStack::Execute(..) = registers.call_stack_ref() {
303 let result = match instruction {
305 Instruction::Call(call) => CallTrait::evaluate(call, self, &mut registers, rng),
307 _ => instruction.evaluate(self, &mut registers),
309 };
310 if let Err(error) = result {
312 bail!("Failed to evaluate instruction ({instruction}): {error}");
313 }
314 }
315
316 let result = match instruction {
318 Instruction::Call(call) => CallTrait::execute(call, self, &mut registers, rng),
320 _ => instruction.execute(self, &mut registers),
322 };
323 if let Err(error) = result {
325 bail!("Failed to execute instruction ({instruction}): {error}");
326 }
327
328 if let Instruction::Call(call) = instruction {
330 if call.is_function_call(self)? {
332 contains_function_call = true;
333 }
334 }
335 }
336 lap!(timer, "Execute the instructions");
337
338 let output_operands = &function.outputs().iter().map(|output| output.operand()).collect::<Vec<_>>();
340 let outputs = output_operands
341 .iter()
342 .map(|operand| {
343 match operand {
344 Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
346 circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
347 ))),
348 Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
350 Operand::ProgramID(program_id) => {
352 Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address(
353 circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?),
354 ))))
355 }
356 Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
358 circuit::Literal::Address(registers.signer_circuit()?),
359 ))),
360 Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
362 circuit::Literal::Address(registers.caller_circuit()?),
363 ))),
364 Operand::BlockHeight => {
366 bail!("Illegal operation: cannot retrieve the block height in a function scope")
367 }
368 Operand::NetworkID => {
370 bail!("Illegal operation: cannot retrieve the network id in a function scope")
371 }
372 Operand::Checksum(_) => {
374 bail!("Illegal operation: cannot retrieve the checksum in a function scope")
375 }
376 Operand::Edition(_) => {
378 bail!("Illegal operation: cannot retrieve the edition in a function scope")
379 }
380 Operand::ProgramOwner(_) => {
382 bail!("Illegal operation: cannot retrieve the program owner in a function scope")
383 }
384 }
385 })
386 .collect::<Result<Vec<_>>>()?;
387 lap!(timer, "Load the outputs");
388
389 let output_registers = output_operands
391 .iter()
392 .map(|operand| match operand {
393 Operand::Register(register) => Some(register.clone()),
394 _ => None,
395 })
396 .collect::<Vec<_>>();
397
398 Self::log_circuit::<A>(format!("Function '{}()'", function.name()));
399
400 let num_function_constraints = A::num_constraints().saturating_sub(num_request_constraints);
402
403 if !contains_function_call {
405 ensure!(A::num_public() == num_public, "Instructions in function injected public variables");
407 }
408
409 let response = circuit::Response::from_outputs(
411 request.signer(),
412 request.network_id(),
413 request.program_id(),
414 request.function_name(),
415 num_inputs,
416 request.tvk(),
417 request.tcm(),
418 outputs,
419 &output_types,
420 &output_registers,
421 );
422 lap!(timer, "Construct the response");
423
424 Self::log_circuit::<A>("Response");
425
426 let num_response_constraints =
428 A::num_constraints().saturating_sub(num_request_constraints).saturating_sub(num_function_constraints);
429
430 Self::log_circuit::<A>("Complete");
431
432 let response = response.eject_value();
434
435 response.outputs().iter().zip_eq(&output_types).try_for_each(|(output, output_type)| {
437 self.matches_value_type(output, output_type)
439 })?;
440
441 if matches!(registers.call_stack_ref(), CallStack::Execute(..) | CallStack::PackageRun(..)) {
443 ensure!(
445 A::num_constraints() > 0 && A::is_satisfied(),
446 "'{}/{}' is not satisfied on the given inputs ({} constraints).",
447 self.program.id(),
448 function.name(),
449 A::num_constraints()
450 );
451 }
452
453 let assignment = A::eject_assignment_and_reset();
455
456 if matches!(registers.call_stack_ref(), CallStack::Synthesize(..) | CallStack::Execute(..)) {
458 if !self.contains_proving_key(function.name()) {
460 self.synthesize_from_assignment(function.name(), &assignment)?;
462 lap!(timer, "Synthesize the {} circuit key", function.name());
463 }
464 }
465 if let CallStack::Authorize(_, _, authorization) = registers.call_stack_ref() {
467 let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
469 authorization.insert_transition(transition)?;
471 lap!(timer, "Save the transition");
472 }
473 else if let CallStack::CheckDeployment(_, _, assignments, _, _) = registers.call_stack_ref() {
475 let metrics = CallMetrics {
477 program_id: *self.program_id(),
478 function_name: *function.name(),
479 num_instructions: function.instructions().len(),
480 num_request_constraints,
481 num_function_constraints,
482 num_response_constraints,
483 };
484 assignments.write().push((assignment, metrics));
486 lap!(timer, "Save the circuit assignment");
487 }
488 else if let CallStack::Execute(_, trace) = registers.call_stack_ref() {
490 registers.ensure_console_and_circuit_registers_match()?;
491
492 let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
494
495 let proving_key = self.get_proving_key(function.name())?;
497 let metrics = CallMetrics {
499 program_id: *self.program_id(),
500 function_name: *function.name(),
501 num_instructions: function.instructions().len(),
502 num_request_constraints,
503 num_function_constraints,
504 num_response_constraints,
505 };
506
507 trace.write().insert_transition(
509 console_request.input_ids(),
510 &transition,
511 (proving_key, assignment),
512 metrics,
513 )?;
514 }
515 else if let CallStack::PackageRun(_, _, assignments) = registers.call_stack_ref() {
517 let metrics = CallMetrics {
519 program_id: *self.program_id(),
520 function_name: *function.name(),
521 num_instructions: function.instructions().len(),
522 num_request_constraints,
523 num_function_constraints,
524 num_response_constraints,
525 };
526 assignments.write().push((assignment, metrics));
528 lap!(timer, "Save the circuit assignment");
529 }
530
531 finish!(timer);
532
533 Ok(response)
535 }
536}
537
538impl<N: Network> Stack<N> {
539 #[allow(unused_variables)]
541 pub(crate) fn log_circuit<A: circuit::Aleo<Network = N>>(scope: impl std::fmt::Display) {
542 #[cfg(debug_assertions)]
543 {
544 use snarkvm_utilities::dev_println;
545
546 use colored::Colorize as _;
547
548 let is_satisfied = if A::is_satisfied() { "✅" } else { "❌" };
550 let (num_constant, num_public, num_private, num_constraints, num_nonzeros) = A::count();
552
553 let scope = scope.to_string().bold();
554
555 dev_println!(
557 "{is_satisfied} {scope:width$} (Constant: {num_constant}, Public: {num_public}, Private: {num_private}, Constraints: {num_constraints}, NonZeros: {num_nonzeros:?})",
558 width = 20
559 );
560 }
561 }
562}