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::BlockTimestamp => {
120 bail!("Illegal operation: cannot retrieve the block timestamp in a closure scope")
121 }
122 Operand::NetworkID => {
124 bail!("Illegal operation: cannot retrieve the network id in a closure scope")
125 }
126 Operand::Checksum(_) => bail!("Illegal operation: cannot retrieve the checksum in a closure scope"),
128 Operand::Edition(_) => bail!("Illegal operation: cannot retrieve the edition in a closure scope"),
130 Operand::ProgramOwner(_) => {
132 bail!("Illegal operation: cannot retrieve the program owner in a closure scope")
133 }
134 }
135 })
136 .collect();
137 lap!(timer, "Load the outputs");
138
139 finish!(timer);
140 outputs
141 }
142
143 pub fn execute_function<A: circuit::Aleo<Network = N>, R: CryptoRng + Rng>(
150 &self,
151 mut call_stack: CallStack<N>,
152 console_caller: Option<ProgramID<N>>,
153 console_root_tvk: Option<Field<N>>,
154 rng: &mut R,
155 ) -> Result<Response<N>> {
156 let timer = timer!("Stack::execute_function");
157
158 A::initialize_global_constants();
160 A::reset();
162
163 if let CallStack::CheckDeployment(_, _, _, constraint_limit, variable_limit) = &call_stack {
166 A::set_constraint_limit(*constraint_limit);
167 A::set_variable_limit(*variable_limit);
168 }
169
170 let console_request = call_stack.pop()?;
172
173 ensure!(
175 **console_request.network_id() == N::ID,
176 "Network ID mismatch. Expected {}, but found {}",
177 N::ID,
178 console_request.network_id()
179 );
180
181 ensure!(console_caller.is_some() == console_root_tvk.is_some());
183 let console_is_root = console_caller.is_none();
185
186 let console_parent = match console_caller {
190 None => console_request.program_id().to_address()?,
192 Some(console_caller) => console_caller.to_address()?,
194 };
195
196 let function = self.get_function(console_request.function_name())?;
198 let num_inputs = function.inputs().len();
200 if num_inputs != console_request.inputs().len() {
202 bail!("Expected {num_inputs} inputs, found {}", console_request.inputs().len())
203 }
204 let input_types = function.input_types();
206 let output_types = function.output_types();
208 lap!(timer, "Retrieve the input and output types");
209
210 console_request.inputs().iter().zip_eq(&input_types).try_for_each(|(input, input_type)| {
212 self.matches_value_type(input, input_type)
214 })?;
215 lap!(timer, "Verify the input types");
216
217 let program_checksum = match self.program().contains_constructor() {
219 true => Some(self.program_checksum_as_field()?),
220 false => None,
221 };
222
223 ensure!(
225 console_request.verify(&input_types, console_is_root, program_checksum),
226 "[Execute] Request is invalid"
227 );
228 lap!(timer, "Verify the console request");
229
230 let mut registers = Registers::new(call_stack, self.get_register_types(function.name())?.clone());
232
233 let console_root_tvk = console_root_tvk.unwrap_or(*console_request.tvk());
235 let root_tvk = circuit::Field::<A>::new(circuit::Mode::Private, console_root_tvk);
237 registers.set_root_tvk(console_root_tvk);
239 registers.set_root_tvk_circuit(root_tvk.clone());
241
242 let program_checksum = program_checksum.map(|c| circuit::Field::<A>::new(circuit::Mode::Public, c));
244
245 use circuit::{Eject, Inject};
246
247 let tpk = circuit::Group::<A>::new(circuit::Mode::Public, console_request.to_tpk());
249 let request = circuit::Request::new(circuit::Mode::Private, console_request.clone());
251
252 let is_root = circuit::Boolean::new(circuit::Mode::Public, console_is_root);
254 let parent = circuit::Address::new(circuit::Mode::Public, console_parent);
256 let caller = Ternary::ternary(&is_root, request.signer(), &parent);
258
259 A::assert(request.verify(&input_types, &tpk, Some(root_tvk), is_root, program_checksum));
261 lap!(timer, "Verify the circuit request");
262
263 registers.set_signer(*console_request.signer());
265 registers.set_signer_circuit(request.signer().clone());
267
268 registers.set_caller(caller.eject_value());
270 registers.set_caller_circuit(caller);
272
273 registers.set_tvk(*console_request.tvk());
275 registers.set_tvk_circuit(request.tvk().clone());
277
278 lap!(timer, "Initialize the registers");
279
280 Self::log_circuit::<A>("Request");
281
282 let num_request_constraints = A::num_constraints();
284
285 let num_public = A::num_public();
287
288 function.inputs().iter().map(|i| i.register()).zip_eq(request.inputs()).try_for_each(|(register, input)| {
290 if let CallStack::Execute(..) = registers.call_stack_ref() {
292 registers.store(self, register, input.eject_value())?;
294 }
295 registers.store_circuit(self, register, input.clone())
297 })?;
298 lap!(timer, "Store the inputs");
299
300 let mut contains_function_call = false;
302
303 for instruction in function.instructions() {
305 if let CallStack::Execute(..) = registers.call_stack_ref() {
307 let result = match instruction {
309 Instruction::Call(call) => CallTrait::evaluate(call, self, &mut registers, rng),
311 _ => instruction.evaluate(self, &mut registers),
313 };
314 if let Err(error) = result {
316 bail!("Failed to evaluate instruction ({instruction}): {error}");
317 }
318 }
319
320 let result = match instruction {
322 Instruction::Call(call) => CallTrait::execute(call, self, &mut registers, rng),
324 _ => instruction.execute(self, &mut registers),
326 };
327 if let Err(error) = result {
329 bail!("Failed to execute instruction ({instruction}): {error}");
330 }
331
332 if let Instruction::Call(call) = instruction {
334 if call.is_function_call(self)? {
336 contains_function_call = true;
337 }
338 }
339 }
340 lap!(timer, "Execute the instructions");
341
342 let output_operands = &function.outputs().iter().map(|output| output.operand()).collect::<Vec<_>>();
344 let outputs = output_operands
345 .iter()
346 .map(|operand| {
347 match operand {
348 Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
350 circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
351 ))),
352 Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
354 Operand::ProgramID(program_id) => {
356 Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address(
357 circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?),
358 ))))
359 }
360 Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
362 circuit::Literal::Address(registers.signer_circuit()?),
363 ))),
364 Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
366 circuit::Literal::Address(registers.caller_circuit()?),
367 ))),
368 Operand::BlockHeight => {
370 bail!("Illegal operation: cannot retrieve the block height in a function scope")
371 }
372 Operand::BlockTimestamp => {
374 bail!("Illegal operation: cannot retrieve the block timestamp in a function scope")
375 }
376 Operand::NetworkID => {
378 bail!("Illegal operation: cannot retrieve the network id in a function scope")
379 }
380 Operand::Checksum(_) => {
382 bail!("Illegal operation: cannot retrieve the checksum in a function scope")
383 }
384 Operand::Edition(_) => {
386 bail!("Illegal operation: cannot retrieve the edition in a function scope")
387 }
388 Operand::ProgramOwner(_) => {
390 bail!("Illegal operation: cannot retrieve the program owner in a function scope")
391 }
392 }
393 })
394 .collect::<Result<Vec<_>>>()?;
395 lap!(timer, "Load the outputs");
396
397 let output_registers = output_operands
399 .iter()
400 .map(|operand| match operand {
401 Operand::Register(register) => Some(register.clone()),
402 _ => None,
403 })
404 .collect::<Vec<_>>();
405
406 Self::log_circuit::<A>(format!("Function '{}()'", function.name()));
407
408 let num_function_constraints = A::num_constraints().saturating_sub(num_request_constraints);
410
411 if !contains_function_call {
413 ensure!(A::num_public() == num_public, "Instructions in function injected public variables");
415 }
416
417 let response = circuit::Response::from_outputs(
419 request.signer(),
420 request.network_id(),
421 request.program_id(),
422 request.function_name(),
423 num_inputs,
424 request.tvk(),
425 request.tcm(),
426 outputs,
427 &output_types,
428 &output_registers,
429 );
430 lap!(timer, "Construct the response");
431
432 Self::log_circuit::<A>("Response");
433
434 let num_response_constraints =
436 A::num_constraints().saturating_sub(num_request_constraints).saturating_sub(num_function_constraints);
437
438 Self::log_circuit::<A>("Complete");
439
440 let response = response.eject_value();
442
443 response.outputs().iter().zip_eq(&output_types).try_for_each(|(output, output_type)| {
445 self.matches_value_type(output, output_type)
447 })?;
448
449 if matches!(registers.call_stack_ref(), CallStack::Execute(..) | CallStack::PackageRun(..)) {
451 ensure!(
453 A::num_constraints() > 0 && A::is_satisfied(),
454 "'{}/{}' is not satisfied on the given inputs ({} constraints).",
455 self.program.id(),
456 function.name(),
457 A::num_constraints()
458 );
459 }
460
461 let assignment = A::eject_assignment_and_reset();
463
464 if matches!(registers.call_stack_ref(), CallStack::Synthesize(..) | CallStack::Execute(..)) {
466 if !self.contains_proving_key(function.name()) {
468 self.synthesize_from_assignment(function.name(), &assignment)?;
470 lap!(timer, "Synthesize the {} circuit key", function.name());
471 }
472 }
473 if let CallStack::Authorize(_, _, authorization) = registers.call_stack_ref() {
475 let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
477 authorization.insert_transition(transition)?;
479 lap!(timer, "Save the transition");
480 }
481 else if let CallStack::CheckDeployment(_, _, assignments, _, _) = registers.call_stack_ref() {
483 let metrics = CallMetrics {
485 program_id: *self.program_id(),
486 function_name: *function.name(),
487 num_instructions: function.instructions().len(),
488 num_request_constraints,
489 num_function_constraints,
490 num_response_constraints,
491 };
492 assignments.write().push((assignment, metrics));
494 lap!(timer, "Save the circuit assignment");
495 }
496 else if let CallStack::Execute(_, trace) = registers.call_stack_ref() {
498 registers.ensure_console_and_circuit_registers_match()?;
499
500 let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
502
503 let proving_key = self.get_proving_key(function.name())?;
505 let metrics = CallMetrics {
507 program_id: *self.program_id(),
508 function_name: *function.name(),
509 num_instructions: function.instructions().len(),
510 num_request_constraints,
511 num_function_constraints,
512 num_response_constraints,
513 };
514
515 trace.write().insert_transition(
517 console_request.input_ids(),
518 &transition,
519 (proving_key, assignment),
520 metrics,
521 )?;
522 }
523 else if let CallStack::PackageRun(_, _, assignments) = registers.call_stack_ref() {
525 let metrics = CallMetrics {
527 program_id: *self.program_id(),
528 function_name: *function.name(),
529 num_instructions: function.instructions().len(),
530 num_request_constraints,
531 num_function_constraints,
532 num_response_constraints,
533 };
534 assignments.write().push((assignment, metrics));
536 lap!(timer, "Save the circuit assignment");
537 }
538
539 finish!(timer);
540
541 Ok(response)
543 }
544}
545
546impl<N: Network> Stack<N> {
547 #[allow(unused_variables)]
549 pub(crate) fn log_circuit<A: circuit::Aleo<Network = N>>(scope: impl std::fmt::Display) {
550 #[cfg(debug_assertions)]
551 {
552 use snarkvm_utilities::dev_println;
553
554 use colored::Colorize as _;
555
556 let is_satisfied = if A::is_satisfied() { "✅" } else { "❌" };
558 let (num_constant, num_public, num_private, num_constraints, num_nonzeros) = A::count();
560
561 let scope = scope.to_string().bold();
562
563 dev_println!(
565 "{is_satisfied} {scope:width$} (Constant: {num_constant}, Public: {num_public}, Private: {num_private}, Constraints: {num_constraints}, NonZeros: {num_nonzeros:?})",
566 width = 20
567 );
568 }
569 }
570}