snarkvm_synthesizer_process/stack/
execute.rs

1// Copyright (c) 2019-2025 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> StackExecute<N> for 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    #[inline]
24    fn execute_closure<A: circuit::Aleo<Network = N>>(
25        &self,
26        closure: &Closure<N>,
27        inputs: &[circuit::Value<A>],
28        call_stack: CallStack<N>,
29        signer: circuit::Address<A>,
30        caller: circuit::Address<A>,
31        tvk: circuit::Field<A>,
32    ) -> Result<Vec<circuit::Value<A>>> {
33        let timer = timer!("Stack::execute_closure");
34
35        // Ensure the call stack is not `Evaluate`.
36        ensure!(!matches!(call_stack, CallStack::Evaluate(..)), "Illegal operation: cannot evaluate in execute mode");
37
38        // Ensure the number of inputs matches the number of input statements.
39        if closure.inputs().len() != inputs.len() {
40            bail!("Expected {} inputs, found {}", closure.inputs().len(), inputs.len())
41        }
42        lap!(timer, "Check the number of inputs");
43
44        // Retrieve the number of public variables in the circuit.
45        let num_public = A::num_public();
46
47        // Initialize the registers.
48        let mut registers = Registers::new(call_stack, self.get_register_types(closure.name())?.clone());
49        // Set the transition signer, as a circuit.
50        registers.set_signer_circuit(signer);
51        // Set the transition caller, as a circuit.
52        registers.set_caller_circuit(caller);
53        // Set the transition view key, as a circuit.
54        registers.set_tvk_circuit(tvk);
55        lap!(timer, "Initialize the registers");
56
57        // Store the inputs.
58        closure.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| {
59            // If the circuit is in execute mode, then store the console input.
60            if let CallStack::Execute(..) = registers.call_stack() {
61                use circuit::Eject;
62                // Assign the console input to the register.
63                registers.store(self, register, input.eject_value())?;
64            }
65            // Assign the circuit input to the register.
66            registers.store_circuit(self, register, input.clone())
67        })?;
68        lap!(timer, "Store the inputs");
69
70        // Execute the instructions.
71        for instruction in closure.instructions() {
72            // If the circuit is in execute mode, then evaluate the instructions.
73            if let CallStack::Execute(..) = registers.call_stack() {
74                // If the evaluation fails, bail and return the error.
75                if let Err(error) = instruction.evaluate(self, &mut registers) {
76                    bail!("Failed to evaluate instruction ({instruction}): {error}");
77                }
78            }
79            // Execute the instruction.
80            instruction.execute(self, &mut registers)?;
81        }
82        lap!(timer, "Execute the instructions");
83
84        // Ensure the number of public variables remains the same.
85        ensure!(A::num_public() == num_public, "Illegal closure operation: instructions injected public variables");
86
87        use circuit::Inject;
88
89        // Load the outputs.
90        let outputs = closure
91            .outputs()
92            .iter()
93            .map(|output| {
94                match output.operand() {
95                    // If the operand is a literal, use the literal directly.
96                    Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
97                        circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
98                    ))),
99                    // If the operand is a register, retrieve the stack value from the register.
100                    Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
101                    // If the operand is the program ID, convert the program ID into an address.
102                    Operand::ProgramID(program_id) => {
103                        Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address(
104                            circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?),
105                        ))))
106                    }
107                    // If the operand is the signer, retrieve the signer from the registers.
108                    Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
109                        circuit::Literal::Address(registers.signer_circuit()?),
110                    ))),
111                    // If the operand is the caller, retrieve the caller from the registers.
112                    Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
113                        circuit::Literal::Address(registers.caller_circuit()?),
114                    ))),
115                    // If the operand is the block height, throw an error.
116                    Operand::BlockHeight => {
117                        bail!("Illegal operation: cannot retrieve the block height in a closure scope")
118                    }
119                    // If the operand is the network id, throw an error.
120                    Operand::NetworkID => {
121                        bail!("Illegal operation: cannot retrieve the network id in a closure scope")
122                    }
123                }
124            })
125            .collect();
126        lap!(timer, "Load the outputs");
127
128        finish!(timer);
129        outputs
130    }
131
132    /// Executes a program function on the given inputs.
133    ///
134    /// Note: To execute a transition, do **not** call this method. Instead, call `Process::execute`.
135    ///
136    /// # Errors
137    /// This method will halt if the given inputs are not the same length as the input statements.
138    #[inline]
139    fn execute_function<A: circuit::Aleo<Network = N>, R: CryptoRng + Rng>(
140        &self,
141        mut call_stack: CallStack<N>,
142        console_caller: Option<ProgramID<N>>,
143        root_tvk: Option<Field<N>>,
144        rng: &mut R,
145    ) -> Result<Response<N>> {
146        let timer = timer!("Stack::execute_function");
147
148        // Ensure the global constants for the Aleo environment are initialized.
149        A::initialize_global_constants();
150        // Ensure the circuit environment is clean.
151        A::reset();
152
153        // If in 'CheckDeployment' mode, set the constraint limit and variable limit.
154        // We do not have to reset it after function calls because `CheckDeployment` mode does not execute those.
155        if let CallStack::CheckDeployment(_, _, _, constraint_limit, variable_limit) = &call_stack {
156            A::set_constraint_limit(*constraint_limit);
157            A::set_variable_limit(*variable_limit);
158        }
159
160        // Retrieve the next request.
161        let console_request = call_stack.pop()?;
162
163        // Ensure the network ID matches.
164        ensure!(
165            **console_request.network_id() == N::ID,
166            "Network ID mismatch. Expected {}, but found {}",
167            N::ID,
168            console_request.network_id()
169        );
170
171        // We can only have a root_tvk if this request was called by another request
172        ensure!(console_caller.is_some() == root_tvk.is_some());
173        // Determine if this is the top-level caller.
174        let console_is_root = console_caller.is_none();
175
176        // Determine the parent.
177        //  - If this execution is the top-level caller, then the parent is the program ID.
178        //  - If this execution is a child caller, then the parent is the caller.
179        let console_parent = match console_caller {
180            // If this execution is the top-level caller, then the parent is the program ID.
181            None => console_request.program_id().to_address()?,
182            // If this execution is a child caller, then the parent is the caller.
183            Some(console_caller) => console_caller.to_address()?,
184        };
185
186        // Retrieve the function from the program.
187        let function = self.get_function(console_request.function_name())?;
188        // Retrieve the number of inputs.
189        let num_inputs = function.inputs().len();
190        // Ensure the number of inputs matches the number of input statements.
191        if num_inputs != console_request.inputs().len() {
192            bail!("Expected {num_inputs} inputs, found {}", console_request.inputs().len())
193        }
194        // Retrieve the input types.
195        let input_types = function.input_types();
196        // Retrieve the output types.
197        let output_types = function.output_types();
198        lap!(timer, "Retrieve the input and output types");
199
200        // Ensure the inputs match their expected types.
201        console_request.inputs().iter().zip_eq(&input_types).try_for_each(|(input, input_type)| {
202            // Ensure the input matches the input type in the function.
203            self.matches_value_type(input, input_type)
204        })?;
205        lap!(timer, "Verify the input types");
206
207        // Ensure the request is well-formed.
208        ensure!(console_request.verify(&input_types, console_is_root), "Request is invalid");
209        lap!(timer, "Verify the console request");
210
211        // Initialize the registers.
212        let mut registers = Registers::new(call_stack, self.get_register_types(function.name())?.clone());
213
214        // Set the root tvk, from a parent request or the current request.
215        // inject the `root_tvk` as `Mode::Private`.
216        if let Some(root_tvk) = root_tvk {
217            registers.set_root_tvk(root_tvk);
218            registers.set_root_tvk_circuit(circuit::Field::<A>::new(circuit::Mode::Private, root_tvk));
219        } else {
220            registers.set_root_tvk(*console_request.tvk());
221            registers.set_root_tvk_circuit(circuit::Field::<A>::new(circuit::Mode::Private, *console_request.tvk()));
222        }
223
224        let root_tvk = Some(registers.root_tvk_circuit()?);
225
226        use circuit::{Eject, Inject};
227
228        // Inject the transition public key `tpk` as `Mode::Public`.
229        let tpk = circuit::Group::<A>::new(circuit::Mode::Public, console_request.to_tpk());
230        // Inject the request as `Mode::Private`.
231        let request = circuit::Request::new(circuit::Mode::Private, console_request.clone());
232
233        // Inject `is_root` as `Mode::Public`.
234        let is_root = circuit::Boolean::new(circuit::Mode::Public, console_is_root);
235        // Inject the parent as `Mode::Public`.
236        let parent = circuit::Address::new(circuit::Mode::Public, console_parent);
237        // Determine the caller.
238        let caller = Ternary::ternary(&is_root, request.signer(), &parent);
239
240        // Ensure the request has a valid signature, inputs, and transition view key.
241        A::assert(request.verify(&input_types, &tpk, root_tvk, is_root));
242        lap!(timer, "Verify the circuit request");
243
244        // Set the transition signer.
245        registers.set_signer(*console_request.signer());
246        // Set the transition signer, as a circuit.
247        registers.set_signer_circuit(request.signer().clone());
248
249        // Set the transition caller.
250        registers.set_caller(caller.eject_value());
251        // Set the transition caller, as a circuit.
252        registers.set_caller_circuit(caller);
253
254        // Set the transition view key.
255        registers.set_tvk(*console_request.tvk());
256        // Set the transition view key, as a circuit.
257        registers.set_tvk_circuit(request.tvk().clone());
258
259        lap!(timer, "Initialize the registers");
260
261        #[cfg(debug_assertions)]
262        Self::log_circuit::<A, _>("Request");
263
264        // Retrieve the number of constraints for verifying the request in the circuit.
265        let num_request_constraints = A::num_constraints();
266
267        // Retrieve the number of public variables in the circuit.
268        let num_public = A::num_public();
269
270        // Store the inputs.
271        function.inputs().iter().map(|i| i.register()).zip_eq(request.inputs()).try_for_each(|(register, input)| {
272            // If the circuit is in execute mode, then store the console input.
273            if let CallStack::Execute(..) = registers.call_stack() {
274                // Assign the console input to the register.
275                registers.store(self, register, input.eject_value())?;
276            }
277            // Assign the circuit input to the register.
278            registers.store_circuit(self, register, input.clone())
279        })?;
280        lap!(timer, "Store the inputs");
281
282        // Initialize a tracker to determine if there are any function calls.
283        let mut contains_function_call = false;
284
285        // Execute the instructions.
286        for instruction in function.instructions() {
287            // If the circuit is in execute mode, then evaluate the instructions.
288            if let CallStack::Execute(..) = registers.call_stack() {
289                // Evaluate the instruction.
290                let result = match instruction {
291                    // If the instruction is a `call` instruction, we need to handle it separately.
292                    Instruction::Call(call) => CallTrait::evaluate(call, self, &mut registers),
293                    // Otherwise, evaluate the instruction normally.
294                    _ => instruction.evaluate(self, &mut registers),
295                };
296                // If the evaluation fails, bail and return the error.
297                if let Err(error) = result {
298                    bail!("Failed to evaluate instruction ({instruction}): {error}");
299                }
300            }
301
302            // Execute the instruction.
303            let result = match instruction {
304                // If the instruction is a `call` instruction, we need to handle it separately.
305                Instruction::Call(call) => CallTrait::execute(call, self, &mut registers, rng),
306                // Otherwise, execute the instruction normally.
307                _ => instruction.execute(self, &mut registers),
308            };
309            // If the execution fails, bail and return the error.
310            if let Err(error) = result {
311                bail!("Failed to execute instruction ({instruction}): {error}");
312            }
313
314            // If the instruction was a function call, then set the tracker to `true`.
315            if let Instruction::Call(call) = instruction {
316                // Check if the call is a function call.
317                if call.is_function_call(self)? {
318                    contains_function_call = true;
319                }
320            }
321        }
322        lap!(timer, "Execute the instructions");
323
324        // Load the outputs.
325        let output_operands = &function.outputs().iter().map(|output| output.operand()).collect::<Vec<_>>();
326        let outputs = output_operands
327            .iter()
328            .map(|operand| {
329                match operand {
330                    // If the operand is a literal, use the literal directly.
331                    Operand::Literal(literal) => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
332                        circuit::Literal::new(circuit::Mode::Constant, literal.clone()),
333                    ))),
334                    // If the operand is a register, retrieve the stack value from the register.
335                    Operand::Register(register) => registers.load_circuit(self, &Operand::Register(register.clone())),
336                    // If the operand is the program ID, convert the program ID into an address.
337                    Operand::ProgramID(program_id) => {
338                        Ok(circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Address(
339                            circuit::Address::new(circuit::Mode::Constant, program_id.to_address()?),
340                        ))))
341                    }
342                    // If the operand is the signer, retrieve the signer from the registers.
343                    Operand::Signer => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
344                        circuit::Literal::Address(registers.signer_circuit()?),
345                    ))),
346                    // If the operand is the caller, retrieve the caller from the registers.
347                    Operand::Caller => Ok(circuit::Value::Plaintext(circuit::Plaintext::from(
348                        circuit::Literal::Address(registers.caller_circuit()?),
349                    ))),
350                    // If the operand is the block height, throw an error.
351                    Operand::BlockHeight => {
352                        bail!("Illegal operation: cannot retrieve the block height in a function scope")
353                    }
354                    // If the operand is the network id, throw an error.
355                    Operand::NetworkID => {
356                        bail!("Illegal operation: cannot retrieve the network id in a function scope")
357                    }
358                }
359            })
360            .collect::<Result<Vec<_>>>()?;
361        lap!(timer, "Load the outputs");
362
363        // Map the output operands into registers.
364        let output_registers = output_operands
365            .iter()
366            .map(|operand| match operand {
367                Operand::Register(register) => Some(register.clone()),
368                _ => None,
369            })
370            .collect::<Vec<_>>();
371
372        #[cfg(debug_assertions)]
373        Self::log_circuit::<A, _>(format!("Function '{}()'", function.name()));
374
375        // Retrieve the number of constraints for executing the function in the circuit.
376        let num_function_constraints = A::num_constraints().saturating_sub(num_request_constraints);
377
378        // If the function does not contain function calls, ensure no new public variables were injected.
379        if !contains_function_call {
380            // Ensure the number of public variables remains the same.
381            ensure!(A::num_public() == num_public, "Instructions in function injected public variables");
382        }
383
384        // Construct the response.
385        let response = circuit::Response::from_outputs(
386            request.network_id(),
387            request.program_id(),
388            request.function_name(),
389            num_inputs,
390            request.tvk(),
391            request.tcm(),
392            outputs,
393            &output_types,
394            &output_registers,
395        );
396        lap!(timer, "Construct the response");
397
398        #[cfg(debug_assertions)]
399        Self::log_circuit::<A, _>("Response");
400
401        // Retrieve the number of constraints for verifying the response in the circuit.
402        let num_response_constraints =
403            A::num_constraints().saturating_sub(num_request_constraints).saturating_sub(num_function_constraints);
404
405        #[cfg(debug_assertions)]
406        Self::log_circuit::<A, _>("Complete");
407
408        // Eject the response.
409        let response = response.eject_value();
410
411        // Ensure the outputs matches the expected value types.
412        response.outputs().iter().zip_eq(&output_types).try_for_each(|(output, output_type)| {
413            // Ensure the output matches its expected type.
414            self.matches_value_type(output, output_type)
415        })?;
416
417        // If the circuit is in `Execute` or `PackageRun` mode, then ensure the circuit is satisfied.
418        if matches!(registers.call_stack(), CallStack::Execute(..) | CallStack::PackageRun(..)) {
419            // If the circuit is empty or not satisfied, then throw an error.
420            ensure!(
421                A::num_constraints() > 0 && A::is_satisfied(),
422                "'{}/{}' is not satisfied on the given inputs ({} constraints).",
423                self.program.id(),
424                function.name(),
425                A::num_constraints()
426            );
427        }
428
429        // Eject the circuit assignment and reset the circuit.
430        let assignment = A::eject_assignment_and_reset();
431
432        // If the circuit is in `Synthesize` or `Execute` mode, synthesize the circuit key, if it does not exist.
433        if matches!(registers.call_stack(), CallStack::Synthesize(..) | CallStack::Execute(..)) {
434            // If the proving key does not exist, then synthesize it.
435            if !self.contains_proving_key(function.name()) {
436                // Add the circuit key to the mapping.
437                self.synthesize_from_assignment(function.name(), &assignment)?;
438                lap!(timer, "Synthesize the {} circuit key", function.name());
439            }
440        }
441        // If the circuit is in `Authorize` mode, then save the transition.
442        if let CallStack::Authorize(_, _, authorization) = registers.call_stack() {
443            // Construct the transition.
444            let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
445            // Add the transition to the authorization.
446            authorization.insert_transition(transition)?;
447            lap!(timer, "Save the transition");
448        }
449        // If the circuit is in `CheckDeployment` mode, then save the assignment.
450        else if let CallStack::CheckDeployment(_, _, ref assignments, _, _) = registers.call_stack() {
451            // Construct the call metrics.
452            let metrics = CallMetrics {
453                program_id: *self.program_id(),
454                function_name: *function.name(),
455                num_instructions: function.instructions().len(),
456                num_request_constraints,
457                num_function_constraints,
458                num_response_constraints,
459            };
460            // Add the assignment to the assignments.
461            assignments.write().push((assignment, metrics));
462            lap!(timer, "Save the circuit assignment");
463        }
464        // If the circuit is in `Execute` mode, then execute the circuit into a transition.
465        else if let CallStack::Execute(_, ref trace) = registers.call_stack() {
466            registers.ensure_console_and_circuit_registers_match()?;
467
468            // Construct the transition.
469            let transition = Transition::from(&console_request, &response, &output_types, &output_registers)?;
470
471            // Retrieve the proving key.
472            let proving_key = self.get_proving_key(function.name())?;
473            // Construct the call metrics.
474            let metrics = CallMetrics {
475                program_id: *self.program_id(),
476                function_name: *function.name(),
477                num_instructions: function.instructions().len(),
478                num_request_constraints,
479                num_function_constraints,
480                num_response_constraints,
481            };
482
483            // Add the transition to the trace.
484            trace.write().insert_transition(
485                console_request.input_ids(),
486                &transition,
487                (proving_key, assignment),
488                metrics,
489            )?;
490        }
491        // If the circuit is in `PackageRun` mode, then save the assignment.
492        else if let CallStack::PackageRun(_, _, ref assignments) = registers.call_stack() {
493            // Construct the call metrics.
494            let metrics = CallMetrics {
495                program_id: *self.program_id(),
496                function_name: *function.name(),
497                num_instructions: function.instructions().len(),
498                num_request_constraints,
499                num_function_constraints,
500                num_response_constraints,
501            };
502            // Add the assignment to the assignments.
503            assignments.write().push((assignment, metrics));
504            lap!(timer, "Save the circuit assignment");
505        }
506
507        finish!(timer);
508
509        // Return the response.
510        Ok(response)
511    }
512}
513
514impl<N: Network> Stack<N> {
515    /// Prints the current state of the circuit.
516    #[cfg(debug_assertions)]
517    pub(crate) fn log_circuit<A: circuit::Aleo<Network = N>, S: Into<String>>(scope: S) {
518        use colored::Colorize;
519
520        // Determine if the circuit is satisfied.
521        let is_satisfied = if A::is_satisfied() { "✅".green() } else { "❌".red() };
522        // Determine the count.
523        let (num_constant, num_public, num_private, num_constraints, num_nonzeros) = A::count();
524
525        // Print the log.
526        println!(
527            "{is_satisfied} {:width$} (Constant: {num_constant}, Public: {num_public}, Private: {num_private}, Constraints: {num_constraints}, NonZeros: {num_nonzeros:?})",
528            scope.into().bold(),
529            width = 20
530        );
531    }
532}