Skip to main content

snarkvm_synthesizer_process/stack/
execute.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::*;
17
18impl<N: Network> Stack<N> {
19    /// Executes a program closure on the given inputs.
20    ///
21    /// # Errors
22    /// This method will halt if the given inputs are not the same length as the input statements.
23    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        // Ensure the call stack is not `Evaluate`.
35        if matches!(call_stack, CallStack::Evaluate(..)) {
36            return Err(anyhow!("Illegal operation: cannot evaluate in execute mode").into());
37        }
38
39        // Ensure the number of inputs matches the number of input statements.
40        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        // Retrieve the number of public variables in the circuit.
46        let num_public = A::num_public();
47
48        // Initialize the registers.
49        let mut registers = Registers::new(call_stack, self.get_register_types(closure.name())?.clone());
50
51        use circuit::Eject;
52
53        // Set the transaction signer.
54        registers.set_signer(signer.eject_value());
55        // Set the transition signer, as a circuit.
56        registers.set_signer_circuit(signer);
57        // Set the transaction caller.
58        registers.set_caller(caller.eject_value());
59        // Set the transition caller, as a circuit.
60        registers.set_caller_circuit(caller);
61        // Set the transition view key.
62        registers.set_tvk(tvk.eject_value());
63        // Set the transition view key, as a circuit.
64        registers.set_tvk_circuit(tvk);
65        lap!(timer, "Initialize the registers");
66
67        // Store the inputs.
68        closure.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| {
69            // If the circuit is in execute mode, then store the console input.
70            if let CallStack::Execute(..) = registers.call_stack_ref() {
71                // Assign the console input to the register.
72                registers.store(self, register, input.eject_value())?;
73            }
74            // Assign the circuit input to the register.
75            registers.store_circuit(self, register, input.clone())
76        })?;
77        lap!(timer, "Store the inputs");
78
79        // Execute the instructions.
80        for (ix, instruction) in closure.instructions().iter().enumerate() {
81            // If the circuit is in execute mode, then evaluate the instructions.
82            if let CallStack::Execute(..) = registers.call_stack_ref() {
83                // If the evaluation fails, bail and return the error.
84                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            // Execute the instruction.
90            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        // Ensure the number of public variables remains the same.
98        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        // Load the outputs.
105        let outputs = closure
106            .outputs()
107            .iter()
108            .map(|output| -> Result<_> {
109                match output.operand() {
110                    // If the operand is a literal, use the literal directly.
111                    Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
112                        circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
113                    ))),
114                    // If the operand is a register, retrieve the stack value from the register.
115                    Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
116                    // If the operand is the program ID, convert the program ID into an address.
117                    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                    // If the operand is the signer, retrieve the signer from the registers.
123                    Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
124                        circuit::Literal::Address(registers.signer_circuit()?),
125                    ))),
126                    // If the operand is the caller, retrieve the caller from the registers.
127                    Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
128                        circuit::Literal::Address(registers.caller_circuit()?),
129                    ))),
130                    // If the operand is the generator, retrieve the Aleo generator.
131                    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                    // If the operand is the generator powers, retrieve the generator powers or the indexed group.
140                    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                    // If the operand is the block height, throw an error.
158                    Operand::BlockHeight => {
159                        bail!("Illegal operation: cannot retrieve the block height in a closure scope")
160                    }
161                    // If the operand is the block timestamp, throw an error.
162                    Operand::BlockTimestamp => {
163                        bail!("Illegal operation: cannot retrieve the block timestamp in a closure scope")
164                    }
165                    // If the operand is the network id, throw an error.
166                    Operand::NetworkID => {
167                        bail!("Illegal operation: cannot retrieve the network id in a closure scope")
168                    }
169                    // If the operand is the checksum, throw an error.
170                    Operand::Checksum(_) => bail!("Illegal operation: cannot retrieve the checksum in a closure scope"),
171                    // If the operand is the edition, throw an error.
172                    Operand::Edition(_) => bail!("Illegal operation: cannot retrieve the edition in a closure scope"),
173                    // If the operand is the program owner, throw an error.
174                    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    /// Executes a program function on the given inputs.
188    ///
189    /// Note: To execute a transition, do **not** call this method. Instead, call `Process::execute`.
190    ///
191    /// # Errors
192    /// This method will halt if the given inputs are not the same length as the input statements.
193    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        // Ensure the global constants for the Aleo environment are initialized.
203        A::initialize_global_constants();
204        // Ensure the circuit environment is clean.
205        A::reset();
206
207        // If in 'CheckDeployment' mode, set the constraint limit and variable limit.
208        // We do not have to reset it after function calls because `CheckDeployment` mode does not execute those.
209        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        // Retrieve the next request.
215        let console_request = call_stack.pop()?;
216
217        // If in Execute mode, push a new translation group for this execution level.
218        // Translations from dynamic calls made at this level will be collected into this group,
219        // and the group will be popped when the transition is inserted.
220        if let CallStack::Execute(_, _, translations) = &call_stack {
221            translations.write().push(Vec::new());
222        }
223
224        // Ensure the network ID matches.
225        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        // We can only have a root_tvk if this request was called by another request
232        if console_caller.is_some() != console_root_tvk.is_some() {
233            return Err(anyhow!("root_tvk requires a caller").into());
234        }
235
236        // Determine if this is the top-level caller.
237        let console_is_root = console_caller.is_none();
238
239        // Determine the parent.
240        //  - If this execution is the top-level caller, then the parent is the program ID.
241        //  - If this execution is a child caller, then the parent is the caller.
242        let console_parent = match console_caller {
243            // If this execution is the top-level caller, then the parent is the program ID.
244            None => console_request.program_id().to_address()?,
245            // If this execution is a child caller, then the parent is the caller.
246            Some(console_caller) => console_caller.to_address()?,
247        };
248
249        // Retrieve the function from the program.
250        let function = self.get_function(console_request.function_name())?;
251        // Retrieve the number of inputs.
252        let num_inputs = function.inputs().len();
253        // Ensure the number of inputs matches the number of input statements.
254        if num_inputs != console_request.inputs().len() {
255            return Err(anyhow!("Expected {num_inputs} inputs, found {}", console_request.inputs().len()).into());
256        }
257        // Retrieve the input types.
258        let input_types = function.input_types();
259        // Retrieve the output types.
260        let output_types = function.output_types();
261        lap!(timer, "Retrieve the input and output types");
262
263        // Ensure the inputs match their expected types.
264        console_request.inputs().iter().zip_eq(&input_types).try_for_each(|(input, input_type)| {
265            // Ensure the input matches the input type in the function.
266            self.matches_value_type(input, input_type)
267        })?;
268        lap!(timer, "Verify the input types");
269
270        // Retrieve the program checksum, if the program has a constructor.
271        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        // Ensure the request is well-formed.
279        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        // Initialize the registers.
285        let mut registers = Registers::new(call_stack, self.get_register_types(function.name())?.clone());
286
287        // Set the root tvk, from a parent request or the current request.
288        let console_root_tvk = console_root_tvk.unwrap_or(*console_request.tvk());
289        // Inject the `root_tvk` as `Mode::Private`.
290        let root_tvk = circuit::Field::<A>::new(circuit::Mode::Private, console_root_tvk);
291        // Set the root tvk.
292        registers.set_root_tvk(console_root_tvk);
293        // Set the root tvk, as a circuit.
294        registers.set_root_tvk_circuit(root_tvk.clone());
295
296        // If a program checksum was passed in, Inject it as `Mode::Public`.
297        let program_checksum = program_checksum.map(|c| circuit::Field::<A>::new(circuit::Mode::Public, c));
298
299        use circuit::{Eject, Inject};
300
301        // Inject the transition public key `tpk` as `Mode::Public`.
302        let tpk = circuit::Group::<A>::new(circuit::Mode::Public, console_request.to_tpk());
303        // Inject the request as `Mode::Private`.
304        let request = circuit::Request::new(circuit::Mode::Private, console_request.clone());
305
306        // Inject `is_root` as `Mode::Public`.
307        let is_root = circuit::Boolean::new(circuit::Mode::Public, console_is_root);
308        // Inject the parent as `Mode::Public`.
309        let parent = circuit::Address::new(circuit::Mode::Public, console_parent);
310        // Determine the caller.
311        let caller = Ternary::ternary(&is_root, request.signer(), &parent);
312
313        // Ensure the request has a valid signature, inputs, and transition view key.
314        A::assert(request.verify(&input_types, &tpk, Some(root_tvk), is_root, program_checksum))?;
315        lap!(timer, "Verify the circuit request");
316
317        // Set the transition signer.
318        registers.set_signer(*console_request.signer());
319        // Set the transition signer, as a circuit.
320        registers.set_signer_circuit(request.signer().clone());
321
322        // Set the transition caller.
323        registers.set_caller(caller.eject_value());
324        // Set the transition caller, as a circuit.
325        registers.set_caller_circuit(caller);
326
327        // Set the transition view key.
328        registers.set_tvk(*console_request.tvk());
329        // Set the transition view key, as a circuit.
330        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        // Retrieve the number of constraints for verifying the request in the circuit.
337        let num_request_constraints = A::num_constraints();
338
339        // Retrieve the number of public variables in the circuit.
340        let num_public = A::num_public();
341
342        // Store the inputs.
343        function.inputs().iter().map(|i| i.register()).zip_eq(request.inputs()).try_for_each(|(register, input)| {
344            // If the circuit is in execute mode, then store the console input.
345            if let CallStack::Execute(..) = registers.call_stack_ref() {
346                // Assign the console input to the register.
347                registers.store(self, register, input.eject_value())?;
348            }
349            // Assign the circuit input to the register.
350            registers.store_circuit(self, register, input.clone())
351        })?;
352        lap!(timer, "Store the inputs");
353
354        // Initialize a tracker to determine if there are any function calls.
355        let mut contains_function_call = false;
356
357        // Execute the instructions.
358        for (ix, instruction) in function.instructions().iter().enumerate() {
359            // If the circuit is in execute mode, then evaluate the instructions.
360            if let CallStack::Execute(..) = registers.call_stack_ref() {
361                // Evaluate the instruction.
362                let result = match instruction {
363                    // If the instruction is a `call` instruction, we need to handle it separately.
364                    Instruction::Call(call) => CallTrait::evaluate(call, self, &mut registers, rng)
365                        .map_err(|e| InstructionEvalError::Call(Box::new(e))),
366                    // If the instruction is a `call.dynamic` instruction, we need to handle it separately.
367                    Instruction::CallDynamic(call_dynamic) => {
368                        // Evaluate the dynamic call.
369                        CallTrait::evaluate(call_dynamic, self, &mut registers, rng)
370                            .map_err(|e| InstructionEvalError::Call(Box::new(e)))
371                    }
372                    // Otherwise, evaluate the instruction normally.
373                    _ => instruction.evaluate(self, &mut registers).map_err(Into::into),
374                };
375                // If the evaluation fails, bail and return the error.
376                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            // Execute the instruction.
383            let result = match instruction {
384                // If the instruction is a `call` instruction, we need to handle it separately.
385                Instruction::Call(call) => CallTrait::execute(call, self, &mut registers, rng)
386                    .map_err(|e| InstructionExecError::Call(Box::new(e))),
387                // If the instruction is a `call.dynamic` instruction, we need to handle it separately.
388                Instruction::CallDynamic(call_dynamic) => {
389                    // Execute the dynamic call.
390                    CallTrait::execute(call_dynamic, self, &mut registers, rng)
391                        .map_err(|e| InstructionExecError::Call(Box::new(e)))
392                }
393                // Otherwise, execute the instruction normally.
394                _ => instruction.execute(self, &mut registers).map_err(InstructionExecError::Exec),
395            };
396            // If the execution fails, bail and return the error.
397            if let Err(error) = result {
398                let err = InstructionError::Exec(error);
399                return Err(IndexedInstructionError::new(ix, format!("{instruction}"), err).into());
400            }
401
402            // If the instruction was a function call, then set the tracker to `true`.
403            match instruction {
404                Instruction::Call(call) => {
405                    if call.is_function_call(self)? {
406                        contains_function_call = true;
407                    }
408                }
409                // A dynamic call is always a function call.
410                Instruction::CallDynamic(_) => {
411                    contains_function_call = true;
412                }
413                _ => {}
414            }
415        }
416        lap!(timer, "Execute the instructions");
417
418        // Load the outputs.
419        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                    // If the operand is a literal, use the literal directly.
425                    Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
426                        circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
427                    ))),
428                    // If the operand is a register, retrieve the stack value from the register.
429                    Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
430                    // If the operand is the program ID, convert the program ID into an address.
431                    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                    // If the operand is the signer, retrieve the signer from the registers.
437                    Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
438                        circuit::Literal::Address(registers.signer_circuit()?),
439                    ))),
440                    // If the operand is the caller, retrieve the caller from the registers.
441                    Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
442                        circuit::Literal::Address(registers.caller_circuit()?),
443                    ))),
444                    // If the operand is the generator, retrieve the Aleo generator.
445                    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                    // If the operand is the generator powers, retrieve the generator powers or the indexed group.
454                    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                    // If the operand is the block height, throw an error.
472                    Operand::BlockHeight => {
473                        bail!("Illegal operation: cannot retrieve the block height in a function scope")
474                    }
475                    // If the operand is the block timestamp, throw an error.
476                    Operand::BlockTimestamp => {
477                        bail!("Illegal operation: cannot retrieve the block timestamp in a function scope")
478                    }
479                    // If the operand is the network id, throw an error.
480                    Operand::NetworkID => {
481                        bail!("Illegal operation: cannot retrieve the network id in a function scope")
482                    }
483                    // If the operand is the checksum, throw an error.
484                    Operand::Checksum(_) => {
485                        bail!("Illegal operation: cannot retrieve the checksum in a function scope")
486                    }
487                    // If the operand is the edition, throw an error.
488                    Operand::Edition(_) => {
489                        bail!("Illegal operation: cannot retrieve the edition in a function scope")
490                    }
491                    // If the operand is the program owner, throw an error.
492                    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        // Map the output operands into registers.
501        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        // Retrieve the number of constraints for executing the function in the circuit.
512        let num_function_constraints = A::num_constraints().saturating_sub(num_request_constraints);
513
514        // If the function does not contain function calls, ensure no new public variables were injected.
515        if !contains_function_call && A::num_public() != num_public {
516            // Ensure the number of public variables remains the same.
517            return Err(anyhow!("Instructions in function injected public variables").into());
518        }
519
520        // Construct the response.
521        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        // Retrieve the number of constraints for verifying the response in the circuit.
538        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        // Eject the response.
544        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        // Ensure the outputs matches the expected value types.
551        response.outputs().iter().zip_eq(&output_types).try_for_each(|(output, output_type)| {
552            // Ensure the output matches its expected type.
553            self.matches_value_type(output, output_type)
554        })?;
555
556        // If the circuit is in `Execute` or `PackageRun` mode, then ensure the circuit is satisfied.
557        if matches!(registers.call_stack_ref(), CallStack::Execute(..) | CallStack::PackageRun(..)) {
558            // If the circuit is empty or not satisfied, then throw an error.
559            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        // Eject the circuit assignment and reset the circuit.
571        let assignment = A::eject_assignment_and_reset();
572
573        // If the circuit is in `Synthesize` or `Execute` mode, synthesize the circuit key, if it does not exist.
574        if matches!(registers.call_stack_ref(), CallStack::Synthesize(..) | CallStack::Execute(..)) {
575            // If the proving key does not exist, then synthesize it.
576            if !self.contains_proving_key(function.name()) {
577                // Add the circuit key to the mapping.
578                self.synthesize_from_assignment(function.name(), &assignment)?;
579                lap!(timer, "Synthesize the {} circuit key", function.name());
580            }
581        }
582        // If the circuit is in `Authorize` mode, then save the transition.
583        if let CallStack::Authorize(_, _, authorization) = registers.call_stack_ref() {
584            // Construct the transition.
585            let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
586
587            // Add the transition to the authorization.
588            authorization.insert_transition(transition)?;
589            lap!(timer, "Save the transition");
590        }
591        // If the circuit is in `CheckDeployment` mode, then save the assignment.
592        else if let CallStack::CheckDeployment(_, _, assignments, _, _) = registers.call_stack_ref() {
593            // Construct the call metrics.
594            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            // Add the assignment to the assignments.
603            assignments.write().push((assignment, metrics));
604            lap!(timer, "Save the circuit assignment");
605        }
606        // If the circuit is in `Execute` mode, then execute the circuit into a transition.
607        else if let CallStack::Execute(_, trace, translations) = registers.call_stack_ref() {
608            registers.ensure_console_and_circuit_registers_match()?;
609
610            // Construct the transition.
611            let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
612
613            // Retrieve the proving key.
614            let proving_key = self.get_proving_key(function.name())?;
615            // Construct the call metrics.
616            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            // Pop the translation group for this execution level.
626            // This group contains translations (with proving keys) from dynamic calls made at this level.
627            let translations_for_transition =
628                translations.write().pop().ok_or_else(|| anyhow!("Translation stack underflow: no group to pop"))?;
629
630            // Add the transition to the trace.
631            trace.write().insert_transition(
632                console_request.input_ids(),
633                &transition,
634                (proving_key, assignment),
635                translations_for_transition,
636                metrics,
637            )?;
638        }
639        // If the circuit is in `PackageRun` mode, then save the assignment.
640        else if let CallStack::PackageRun(_, _, assignments) = registers.call_stack_ref() {
641            // Construct the call metrics.
642            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            // Add the assignment to the assignments.
651            assignments.write().push((assignment, metrics));
652            lap!(timer, "Save the circuit assignment");
653        }
654
655        finish!(timer);
656
657        // Return the response.
658        Ok(response)
659    }
660}
661
662impl<N: Network> Stack<N> {
663    /// Prints the current state of the circuit.
664    #[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            // Determine if the circuit is satisfied.
676            let is_satisfied = if A::is_satisfied() { "✅" } else { "❌" };
677            // Determine the count.
678            let (num_constant, num_public, num_private, num_constraints, num_nonzeros) = A::count();
679
680            let scope = scope.to_string().bold();
681
682            // Print the log.
683            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}