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>>, StackExecError> {
32 let timer = timer!("Stack::execute_closure");
33
34 if matches!(call_stack, CallStack::Evaluate(..)) {
36 return Err(anyhow!("Illegal operation: cannot evaluate in execute mode").into());
37 }
38
39 if closure.inputs().len() != inputs.len() {
41 return Err(anyhow!("Expected {} inputs, found {}", closure.inputs().len(), inputs.len()).into());
42 }
43 lap!(timer, "Check the number of inputs");
44
45 let num_public = A::num_public();
47
48 let mut registers = Registers::new(call_stack, self.get_register_types(closure.name())?.clone());
50
51 use circuit::Eject;
52
53 registers.set_signer(signer.eject_value());
55 registers.set_signer_circuit(signer);
57 registers.set_caller(caller.eject_value());
59 registers.set_caller_circuit(caller);
61 registers.set_tvk(tvk.eject_value());
63 registers.set_tvk_circuit(tvk);
65 lap!(timer, "Initialize the registers");
66
67 closure.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| {
69 if let CallStack::Execute(..) = registers.call_stack_ref() {
71 registers.store(self, register, input.eject_value())?;
73 }
74 registers.store_circuit(self, register, input.clone())
76 })?;
77 lap!(timer, "Store the inputs");
78
79 for (ix, instruction) in closure.instructions().iter().enumerate() {
81 if let CallStack::Execute(..) = registers.call_stack_ref() {
83 if let Err(error) = instruction.evaluate(self, &mut registers) {
85 let err = InstructionError::Eval(error.into());
86 return Err(IndexedInstructionError::new(ix, format!("{instruction}"), err).into());
87 }
88 }
89 if let Err(error) = instruction.execute(self, &mut registers) {
91 let err = InstructionError::Exec(error.into());
92 return Err(IndexedInstructionError::new(ix, format!("{instruction}"), err).into());
93 }
94 }
95 lap!(timer, "Execute the instructions");
96
97 if A::num_public() != num_public {
99 return Err(anyhow!("Illegal closure operation: instructions injected public variables").into());
100 }
101
102 use circuit::Inject;
103
104 let outputs = closure
106 .outputs()
107 .iter()
108 .map(|output| -> Result<_> {
109 match output.operand() {
110 Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
112 circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
113 ))),
114 Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
116 Operand::ProgramID(program_id) => {
118 Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address(
119 circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?),
120 ))))
121 }
122 Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
124 circuit::Literal::Address(registers.signer_circuit()?),
125 ))),
126 Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
128 circuit::Literal::Address(registers.caller_circuit()?),
129 ))),
130 Operand::AleoGenerator => A::g_powers()
132 .first()
133 .map(|element| {
134 circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Group(
135 element.clone(),
136 )))
137 })
138 .ok_or_else(|| anyhow!("Failed to retrieve the Aleo generator")),
139 Operand::AleoGeneratorPowers(index) => match index {
141 None => Ok(circuit::Value::Plaintext(circuit::Plaintext::Array(
142 A::g_powers()
143 .into_iter()
144 .map(|element| circuit::Plaintext::from(circuit::Literal::Group(element)))
145 .collect(),
146 OnceCell::new(),
147 ))),
148 Some(index) => A::g_powers()
149 .get(**index as usize)
150 .map(|element| {
151 circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Group(
152 element.clone(),
153 )))
154 })
155 .ok_or_else(|| anyhow!("Index {index} out of bounds for Aleo generator")),
156 },
157 Operand::BlockHeight => {
159 bail!("Illegal operation: cannot retrieve the block height in a closure scope")
160 }
161 Operand::BlockTimestamp => {
163 bail!("Illegal operation: cannot retrieve the block timestamp in a closure scope")
164 }
165 Operand::NetworkID => {
167 bail!("Illegal operation: cannot retrieve the network id in a closure scope")
168 }
169 Operand::Checksum(_) => bail!("Illegal operation: cannot retrieve the checksum in a closure scope"),
171 Operand::Edition(_) => bail!("Illegal operation: cannot retrieve the edition in a closure scope"),
173 Operand::ProgramOwner(_) => {
175 bail!("Illegal operation: cannot retrieve the program owner in a closure scope")
176 }
177 }
178 })
179 .map(|res| res.map_err(StackExecError::Anyhow))
180 .collect();
181 lap!(timer, "Load the outputs");
182
183 finish!(timer);
184 outputs
185 }
186
187 pub fn execute_function<A: circuit::Aleo<Network = N>, R: CryptoRng + Rng>(
194 &self,
195 mut call_stack: CallStack<N>,
196 console_caller: Option<ProgramID<N>>,
197 console_root_tvk: Option<Field<N>>,
198 rng: &mut R,
199 ) -> Result<Response<N>, StackExecError> {
200 let timer = timer!("Stack::execute_function");
201
202 A::initialize_global_constants();
204 A::reset();
206
207 if let CallStack::CheckDeployment(_, _, _, constraint_limit, variable_limit) = &call_stack {
210 A::set_constraint_limit(*constraint_limit);
211 A::set_variable_limit(*variable_limit);
212 }
213
214 let console_request = call_stack.pop()?;
216
217 if let CallStack::Execute(_, _, translations) = &call_stack {
221 translations.write().push(Vec::new());
222 }
223
224 if **console_request.network_id() != N::ID {
226 return Err(
227 anyhow!("Network ID mismatch. Expected {}, but found {}", N::ID, console_request.network_id()).into()
228 );
229 }
230
231 if console_caller.is_some() != console_root_tvk.is_some() {
233 return Err(anyhow!("root_tvk requires a caller").into());
234 }
235
236 let console_is_root = console_caller.is_none();
238
239 let console_parent = match console_caller {
243 None => console_request.program_id().to_address()?,
245 Some(console_caller) => console_caller.to_address()?,
247 };
248
249 let function = self.get_function(console_request.function_name())?;
251 let num_inputs = function.inputs().len();
253 if num_inputs != console_request.inputs().len() {
255 return Err(anyhow!("Expected {num_inputs} inputs, found {}", console_request.inputs().len()).into());
256 }
257 let input_types = function.input_types();
259 let output_types = function.output_types();
261 lap!(timer, "Retrieve the input and output types");
262
263 console_request.inputs().iter().zip_eq(&input_types).try_for_each(|(input, input_type)| {
265 self.matches_value_type(input, input_type)
267 })?;
268 lap!(timer, "Verify the input types");
269
270 let program_checksum = match self.program().contains_constructor() {
272 true => Some(self.program_checksum_as_field()?),
273 false => None,
274 };
275
276 let call_stack_type = call_stack.type_as_string();
277
278 if !console_request.verify(&input_types, console_is_root, program_checksum) {
280 return Err(anyhow!("[Execute] Request is invalid").into());
281 }
282 lap!(timer, "Verify the console request");
283
284 let mut registers = Registers::new(call_stack, self.get_register_types(function.name())?.clone());
286
287 let console_root_tvk = console_root_tvk.unwrap_or(*console_request.tvk());
289 let root_tvk = circuit::Field::<A>::new(circuit::Mode::Private, console_root_tvk);
291 registers.set_root_tvk(console_root_tvk);
293 registers.set_root_tvk_circuit(root_tvk.clone());
295
296 let program_checksum = program_checksum.map(|c| circuit::Field::<A>::new(circuit::Mode::Public, c));
298
299 use circuit::{Eject, Inject};
300
301 let tpk = circuit::Group::<A>::new(circuit::Mode::Public, console_request.to_tpk());
303 let request = circuit::Request::new(circuit::Mode::Private, console_request.clone());
305
306 let is_root = circuit::Boolean::new(circuit::Mode::Public, console_is_root);
308 let parent = circuit::Address::new(circuit::Mode::Public, console_parent);
310 let caller = Ternary::ternary(&is_root, request.signer(), &parent);
312
313 A::assert(request.verify(&input_types, &tpk, Some(root_tvk), is_root, program_checksum))?;
315 lap!(timer, "Verify the circuit request");
316
317 registers.set_signer(*console_request.signer());
319 registers.set_signer_circuit(request.signer().clone());
321
322 registers.set_caller(caller.eject_value());
324 registers.set_caller_circuit(caller);
326
327 registers.set_tvk(*console_request.tvk());
329 registers.set_tvk_circuit(request.tvk().clone());
331
332 lap!(timer, "Initialize the registers");
333
334 Self::log_circuit::<A>("Request", &call_stack_type);
335
336 let num_request_constraints = A::num_constraints();
338
339 let num_public = A::num_public();
341
342 function.inputs().iter().map(|i| i.register()).zip_eq(request.inputs()).try_for_each(|(register, input)| {
344 if let CallStack::Execute(..) = registers.call_stack_ref() {
346 registers.store(self, register, input.eject_value())?;
348 }
349 registers.store_circuit(self, register, input.clone())
351 })?;
352 lap!(timer, "Store the inputs");
353
354 let mut contains_function_call = false;
356
357 for (ix, instruction) in function.instructions().iter().enumerate() {
359 if let CallStack::Execute(..) = registers.call_stack_ref() {
361 let result = match instruction {
363 Instruction::Call(call) => CallTrait::evaluate(call, self, &mut registers, rng)
365 .map_err(|e| InstructionEvalError::Call(Box::new(e))),
366 Instruction::CallDynamic(call_dynamic) => {
368 CallTrait::evaluate(call_dynamic, self, &mut registers, rng)
370 .map_err(|e| InstructionEvalError::Call(Box::new(e)))
371 }
372 _ => instruction.evaluate(self, &mut registers).map_err(Into::into),
374 };
375 if let Err(error) = result {
377 let err = InstructionError::Eval(error);
378 return Err(IndexedInstructionError::new(ix, format!("{instruction}"), err).into());
379 }
380 }
381
382 let result = match instruction {
384 Instruction::Call(call) => CallTrait::execute(call, self, &mut registers, rng)
386 .map_err(|e| InstructionExecError::Call(Box::new(e))),
387 Instruction::CallDynamic(call_dynamic) => {
389 CallTrait::execute(call_dynamic, self, &mut registers, rng)
391 .map_err(|e| InstructionExecError::Call(Box::new(e)))
392 }
393 _ => instruction.execute(self, &mut registers).map_err(InstructionExecError::Exec),
395 };
396 if let Err(error) = result {
398 let err = InstructionError::Exec(error);
399 return Err(IndexedInstructionError::new(ix, format!("{instruction}"), err).into());
400 }
401
402 match instruction {
404 Instruction::Call(call) => {
405 if call.is_function_call(self)? {
406 contains_function_call = true;
407 }
408 }
409 Instruction::CallDynamic(_) => {
411 contains_function_call = true;
412 }
413 _ => {}
414 }
415 }
416 lap!(timer, "Execute the instructions");
417
418 let output_operands = &function.outputs().iter().map(|output| output.operand()).collect::<Vec<_>>();
420 let outputs = output_operands
421 .iter()
422 .map(|operand| {
423 match operand {
424 Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
426 circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
427 ))),
428 Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
430 Operand::ProgramID(program_id) => {
432 Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address(
433 circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?),
434 ))))
435 }
436 Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
438 circuit::Literal::Address(registers.signer_circuit()?),
439 ))),
440 Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
442 circuit::Literal::Address(registers.caller_circuit()?),
443 ))),
444 Operand::AleoGenerator => A::g_powers()
446 .first()
447 .map(|element| {
448 circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Group(
449 element.clone(),
450 )))
451 })
452 .ok_or_else(|| anyhow!("Failed to retrieve the Aleo generator")),
453 Operand::AleoGeneratorPowers(index) => match index {
455 None => Ok(circuit::Value::Plaintext(circuit::Plaintext::Array(
456 A::g_powers()
457 .into_iter()
458 .map(|element| circuit::Plaintext::from(circuit::Literal::Group(element)))
459 .collect(),
460 OnceCell::new(),
461 ))),
462 Some(index) => A::g_powers()
463 .get(**index as usize)
464 .map(|element| {
465 circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Group(
466 element.clone(),
467 )))
468 })
469 .ok_or_else(|| anyhow!("Index {index} out of bounds for Aleo generator")),
470 },
471 Operand::BlockHeight => {
473 bail!("Illegal operation: cannot retrieve the block height in a function scope")
474 }
475 Operand::BlockTimestamp => {
477 bail!("Illegal operation: cannot retrieve the block timestamp in a function scope")
478 }
479 Operand::NetworkID => {
481 bail!("Illegal operation: cannot retrieve the network id in a function scope")
482 }
483 Operand::Checksum(_) => {
485 bail!("Illegal operation: cannot retrieve the checksum in a function scope")
486 }
487 Operand::Edition(_) => {
489 bail!("Illegal operation: cannot retrieve the edition in a function scope")
490 }
491 Operand::ProgramOwner(_) => {
493 bail!("Illegal operation: cannot retrieve the program owner in a function scope")
494 }
495 }
496 })
497 .collect::<Result<Vec<_>>>()?;
498 lap!(timer, "Load the outputs");
499
500 let output_registers = output_operands
502 .iter()
503 .map(|operand| match operand {
504 Operand::Register(register) => Some(register.clone()),
505 _ => None,
506 })
507 .collect::<Vec<_>>();
508
509 Self::log_circuit::<A>(format!("Function '{}()'", function.name()), &call_stack_type);
510
511 let num_function_constraints = A::num_constraints().saturating_sub(num_request_constraints);
513
514 if !contains_function_call && A::num_public() != num_public {
516 return Err(anyhow!("Instructions in function injected public variables").into());
518 }
519
520 let response = circuit::Response::from_outputs(
522 request.signer(),
523 request.network_id(),
524 request.program_id(),
525 request.function_name(),
526 num_inputs,
527 request.tvk(),
528 request.tcm(),
529 outputs,
530 &output_types,
531 &output_registers,
532 );
533 lap!(timer, "Construct the response");
534
535 Self::log_circuit::<A>("Response", &call_stack_type);
536
537 let num_response_constraints =
539 A::num_constraints().saturating_sub(num_request_constraints).saturating_sub(num_function_constraints);
540
541 Self::log_circuit::<A>("Complete", &call_stack_type);
542
543 let response = response.eject_value();
545
546 if response.outputs().len() != output_types.len() {
547 return Err(anyhow!("Number of outputs does not match number of output types").into());
548 }
549
550 response.outputs().iter().zip_eq(&output_types).try_for_each(|(output, output_type)| {
552 self.matches_value_type(output, output_type)
554 })?;
555
556 if matches!(registers.call_stack_ref(), CallStack::Execute(..) | CallStack::PackageRun(..)) {
558 if A::num_constraints() == 0 || !A::is_satisfied() {
560 return Err(anyhow!(
561 "'{}/{}' is not satisfied on the given inputs ({} constraints).",
562 self.program.id(),
563 function.name(),
564 A::num_constraints()
565 )
566 .into());
567 }
568 }
569
570 let assignment = A::eject_assignment_and_reset();
572
573 if matches!(registers.call_stack_ref(), CallStack::Synthesize(..) | CallStack::Execute(..)) {
575 if !self.contains_proving_key(function.name()) {
577 self.synthesize_from_assignment(function.name(), &assignment)?;
579 lap!(timer, "Synthesize the {} circuit key", function.name());
580 }
581 }
582 if let CallStack::Authorize(_, _, authorization) = registers.call_stack_ref() {
584 let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
586
587 authorization.insert_transition(transition)?;
589 lap!(timer, "Save the transition");
590 }
591 else if let CallStack::CheckDeployment(_, _, assignments, _, _) = registers.call_stack_ref() {
593 let metrics = CallMetrics {
595 program_id: *self.program_id(),
596 function_name: *function.name(),
597 num_instructions: function.instructions().len(),
598 num_request_constraints,
599 num_function_constraints,
600 num_response_constraints,
601 };
602 assignments.write().push((assignment, metrics));
604 lap!(timer, "Save the circuit assignment");
605 }
606 else if let CallStack::Execute(_, trace, translations) = registers.call_stack_ref() {
608 registers.ensure_console_and_circuit_registers_match()?;
609
610 let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
612
613 let proving_key = self.get_proving_key(function.name())?;
615 let metrics = CallMetrics {
617 program_id: *self.program_id(),
618 function_name: *function.name(),
619 num_instructions: function.instructions().len(),
620 num_request_constraints,
621 num_function_constraints,
622 num_response_constraints,
623 };
624
625 let translations_for_transition =
628 translations.write().pop().ok_or_else(|| anyhow!("Translation stack underflow: no group to pop"))?;
629
630 trace.write().insert_transition(
632 console_request.input_ids(),
633 &transition,
634 (proving_key, assignment),
635 translations_for_transition,
636 metrics,
637 )?;
638 }
639 else if let CallStack::PackageRun(_, _, assignments) = registers.call_stack_ref() {
641 let metrics = CallMetrics {
643 program_id: *self.program_id(),
644 function_name: *function.name(),
645 num_instructions: function.instructions().len(),
646 num_request_constraints,
647 num_function_constraints,
648 num_response_constraints,
649 };
650 assignments.write().push((assignment, metrics));
652 lap!(timer, "Save the circuit assignment");
653 }
654
655 finish!(timer);
656
657 Ok(response)
659 }
660}
661
662impl<N: Network> Stack<N> {
663 #[allow(unused_variables)]
665 pub(crate) fn log_circuit<A: circuit::Aleo<Network = N>>(
666 scope: impl std::fmt::Display,
667 call_stack_type: impl std::fmt::Display,
668 ) {
669 #[cfg(debug_assertions)]
670 {
671 use snarkvm_utilities::dev_println;
672
673 use colored::Colorize as _;
674
675 let is_satisfied = if A::is_satisfied() { "✅" } else { "❌" };
677 let (num_constant, num_public, num_private, num_constraints, num_nonzeros) = A::count();
679
680 let scope = scope.to_string().bold();
681
682 dev_println!(
684 "{is_satisfied} {call_stack_type:width$} {scope:width$} (Constant: {num_constant}, Public: {num_public}, Private: {num_private}, Constraints: {num_constraints}, NonZeros: {num_nonzeros:?})",
685 width = 20
686 );
687 }
688 }
689}